/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pedigreereconstruction;

import distribution.RelationshipDistribution;
import java.io.*;
import java.util.*;
import java.util.logging.*;
import org.apache.commons.cli.*;
import relationships.ProbabilisticRelationship;
import relationships.Relationship;
import relationships.RelationshipTypes;
import umontreal.iro.lecuyer.probdist.*;

/**
 *
 * @author arthiramachandran
 */
public class PedigreeReconstruction {

    public static HashMap<String, Individual> individuals =
            new HashMap<String, Individual>();
    // key = individual's id
    // not sure why i need this 'realtionships' variable
    //public static ArrayList<HashMap<String, ProbabilisticRelationship>> relationships =
    //        new ArrayList<HashMap<String, ProbabilisticRelationship>>(6);
    public static HashMap<String, ProbabilisticRelationship> relationships =
            new HashMap<String, ProbabilisticRelationship>();
    // key = id1 + "-" + id2
    // key is the most connected individual in the pedigree
    /*public static HashMap<Integer, ArrayList<ProbabilisticPedigree>> pedigrees =
    new HashMap<Integer, ArrayList<ProbabilisticPedigree>>();
     * 
     */
    /*public static HashMap<ProbabilisticRelationship, ArrayList<ProbabilisticPedigree>> pedigrees =
    new HashMap<ProbabilisticRelationship, ArrayList<ProbabilisticPedigree>>();
     * 
     */
    public static HashMap<ProbabilisticRelationship, TreeSet<ProbabilisticPedigree>> pedigrees =
            new HashMap<ProbabilisticRelationship, TreeSet<ProbabilisticPedigree>>();
    public static HashMap<ProbabilisticPedigree, TreeSet<ProbabilisticPedigree>> alreadyAttemptedCombination =
            new HashMap< ProbabilisticPedigree, TreeSet<ProbabilisticPedigree>>();
    //public static HashMap<ArrayList<Relationship>, HashMap<ArrayList<Relationship>, Boolean>> alreadyAttemptedCombination =
    //       new HashMap<ArrayList<Relationship>, HashMap<ArrayList<Relationship>, Boolean>>();
    /*public static RelationshipDistribution parent_child = null,
    grandparent_grandchild = null,
    siblings = null,
    halfsiblings = null,
    avuncular = null,
    unrelated = null;
     * 
     */
    public static HashMap<RelationshipTypes, RelationshipDistribution> knownPairwiseDistributions =
            new HashMap<RelationshipTypes, RelationshipDistribution>();
    private static ArrayList<String> pairFileNames = new ArrayList<String>();
    private static ArrayList<String> trioFileNames = new ArrayList<String>();
    private static String trainingRoot = "";
    private static String ppllist = "";
    // constants
    public static final int PARENT_CHILD = 0,
            GRANDPARENT_GRANDCHILD = 1,
            SIBLINGS = 2,
            HALF_SIBLINGS = 3,
            UNRELATED = 4,
            AVUNCULAR = 5;
    public static final int GP_P_GC = 0, P_C_P = 1, C_P_C = 2, S_P_C = 3, S_S_S =
            4;
    private static double falseDiscoveryRate = 0.05; //1 + 0.1; // TODO: make this an input
    private static double HIGHLY_LIKELY = 10;
    private static int missingID = -1;
    // TODO: check if im using FDR correctly
    // for convenience
    public static RelationshipTypes parent_child =
            new RelationshipTypes(1, 0, 1);
    public static RelationshipTypes unrelated =
            new RelationshipTypes(Integer.MAX_VALUE, Integer.MAX_VALUE, 0);
    public static RelationshipTypes siblings =
            new RelationshipTypes(1, 1, 2);
    public static RelationshipTypes grandparent_grandchild =
            new RelationshipTypes(2, 0, 1);
    public static RelationshipTypes halfsiblings =
            new RelationshipTypes(1, 1, 1);
    public static RelationshipTypes avuncular = new RelationshipTypes(2, 1, 2);
    private static HashMap<ProbabilisticRelationship, Boolean> relationshipsInDistributions =
            new HashMap<ProbabilisticRelationship, Boolean>();

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        /*
         * 1. Read in files with total sharing, etc and input into array of relationships
         * 2. Split pedigree into training/test
         *      (Eventually) Allow option to accept parameters from external source
         * 3. For test set, compute parameters for the various relationships (ie set parameters of the RelationshipDistributions)
         * 4. Calculate probability of various relationships for each of the pairs
         * 5. (Likely will need to do) Prune relationships for those which are extremely unlikely
         * 6. Combine first-degree likely relationships into 3 person pedigrees
         *      Get rid of relationships not relevant to that particular pedigree
         * 7. ...
         */
        /**
         * inputs:
         * TODO: describe inputs better
         * pairprob : files with the pair probabilities
         * gpgc : file with probabilities for grandparent-parent-child trios
         */
        parseArgs(args);

        initializeRelationshipDistributions();
        //readInputData("R Files/K-114.ppl.lst");
        //readInputData("R Files/K-232.ppl.lst");
        readInputData(ppllist);
        //readInputData("R Files/ppl.lst");
        System.out.println(
                "Number of smaller pedigrees = " + sizeOf(pedigrees));
        System.out.println(
                "Actual number of smaller pedigrees = " + realSizeOf(pedigrees));

        // Remove low prob trios?
        //removeLowProbTrios();
        System.out.println(
                "Number of smaller pedigrees = " + sizeOf(pedigrees));


        // collection of sets of pedigrees with common people
        // need to make this quicker - maybe index by the individuals present in it? That might make it bulkier though..
        // TODO: simplify how many trios we're looking at
        HashSet<HashSet<Pedigree>> pedigreesWithCommonIndividuals =
                getPedigreesWithCommonIndividualsMaxFirst(pedigrees);
        System.out.println(
                "Number of sets of pedigrees to combine = " + pedigreesWithCommonIndividuals.size());

        // TODO: loop this
        //int numberOfPedigreesToCombine = pedigreesWithCommonIndividuals.size();
        int prevNumberOfPedigreesToCombine =
                pedigreesWithCommonIndividuals.size();
        int differenceInNumberOfPedigreesToCombine = 1;
        int iteration = 1;

        while (pedigreesWithCommonIndividuals.size() > 0) {
            //while (differenceInNumberOfPedigreesToCombine > 0) {
            /*System.out.println("____________________");
            for (ArrayList<ProbabilisticPedigree> list : pedigrees.values()) {
            for (ProbabilisticPedigree p : list) {
            p.computeLogProbability();
            System.out.println(p.printGraph());
            }
            }
            System.out.println("____________________");
             * 
             */

            System.out.println("Iteration: " + iteration);
            System.out.println("Time: " + new Date().toString());
            System.out.println(
                    "Number of smaller pedigrees = " + sizeOf(pedigrees));
            System.out.println(
                    "Actual number of smaller pedigrees = " + realSizeOf(pedigrees));
            PedigreeReconstructionNoRemove.combinePedigreesWithCommonIndividualsNoRemove(pedigreesWithCommonIndividuals, false);
            //printEverything();
            iteration = iteration + 1;
            pedigreesWithCommonIndividuals =
                    getPedigreesWithCommonIndividualsMaxFirst(pedigrees);
            System.out.println(
                    "Number of sets of pedigrees to combine = " + pedigreesWithCommonIndividuals.size());
            differenceInNumberOfPedigreesToCombine =
                    prevNumberOfPedigreesToCombine - pedigreesWithCommonIndividuals.size();
            prevNumberOfPedigreesToCombine =
                    pedigreesWithCommonIndividuals.size();

            int maxPedigreeSize = getMaxPedigreeSize(pedigrees);
            System.out.println("maxPedigreeSize: " + maxPedigreeSize);
            if (maxPedigreeSize > 25) {
                break;
            }
            //break;
            //ArrayList<ProbabilisticPedigree> highlyLikelyPedigrees =
            //        getHighProbabilityPedigrees();
            //updateDistributions(highlyLikelyPedigrees);
            //updatePedigreesWithDistributions(); - might not need this
            /*for (HashMap<String, Pedigree> list : pedigreesWithCommonIndividuals.values()) {
            int contains_r = 0;
            for (Pedigree p : list.values()) {
            if (p.containsRelationship("K6001", "K5910", PedigreeReconstruction.siblings)) {
            contains_r++;
            }
            }
            if (contains_r >= 2) {
            System.out.println("-----------------------");
            for (Pedigree p : list.values()) {
            System.out.println(p.printGraph());
            }
            }
            }
             * 
             */

            /*
            for (HashMap<String, Pedigree> list : pedigreesWithCommonIndividuals.values()) {
            System.out.println("-----------------------");
            
            for (Pedigree p : list.values()) {
            System.out.println(p.printGraph());
            
            }
            }
             * 
             */

        }
        // Print the final pedigree(s)
        // TODO: why are some of the pedigrees of probability 0?
        //System.exit(0);
        int numIndividuals = 0;
        //System.out.println("Creating Gold Standard pedigree: ");
        //GoldStandard.createPedigreeFromFamFile("Soapv2.fam");
        // TODO: keep only the most likely pedigrees for each set of individuals
        // order by likelihood (over null)
        // ignore pedigrees which contain elements that are in previous accepted peds
        // not the maximal pedigrees - the total maximal pedigrees

        // print max pedigree to file
        try {
            System.out.println(pairFileNames.get(UNRELATED));
            System.out.println(pairFileNames.get(UNRELATED).substring(8));
            File file =
                    new File("output." + pairFileNames.get(UNRELATED).substring(8) + ".training." + trainingRoot);
            FileWriter fw = new FileWriter(file.getAbsoluteFile());
            BufferedWriter bw = new BufferedWriter(fw);
            ProbabilisticPedigree p = getMaximalFinalPedigree();
            bw.write(p.getNumberOfIndividuals() + "\t"
                    + p.likelihood + "\t"
                    + p.logLikelihoodOfNull() + "\t"
                    + p.logModelLikelihoodOverNull() + "\n");
            bw.write(p.printGraph());
            bw.close();
        } catch (IOException ex) {
            Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
        }

        System.exit(1);
        HashMap<ProbabilisticPedigree, Double> finalPedigrees =
                getMaximalFinalPedigrees();
        //System.exit(0);

        System.out.println(
                "--------------------");
        System.out.println(
                "Final Pedigree(s) : " + sizeOf(pedigrees));



        for (ProbabilisticPedigree p : finalPedigrees.keySet()) {
            try {
                double alpha =
                        PedigreeReconstruction.logFalseDiscoveryRate(p);
                numIndividuals += p.getNumberOfIndividuals();
                //p.computeLogProbability();
                System.out.println(
                        p.getNumberOfIndividuals() + "\t"
                        + p.likelihood + "\t"
                        + p.logLikelihoodOfNull() + "\t"
                        + p.logModelLikelihoodOverNull() + "\t"
                        + alpha);
                System.out.println(p.printGraph());

                //DrawGraph frame = new DrawGraph(p);
                //frame.draw(p);

            } catch (Exception ex) {
                Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

        System.out.println(
                "Number: " + numIndividuals);
        //System.exit(0);

        System.out.println(
                "--------------------");
        numIndividuals = 0;

        System.out.println(
                "Final Pedigree(s) : " + sizeOf(pedigrees));

        ArrayList<ProbabilisticPedigree> done =
                new ArrayList<ProbabilisticPedigree>();

        for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
            for (ProbabilisticPedigree p : list) {
                if (p.getNumberOfNonMissingIndividuals() < 3) {
                    continue;
                }
                if (done.contains(p)) {
                    continue;
                }
                try {
                    double alpha =
                            PedigreeReconstruction.logFalseDiscoveryRate(p);
                    // TODO: verify this
                    if (p.logModelLikelihoodOverNull() > alpha) {
                        //if (!p.containsRelationship("K6001", "K5910", PedigreeReconstruction.siblings)) {
                        //    continue;
                        //}
                        numIndividuals += p.getNumberOfIndividuals();
                        p.computeLogProbability();
                        System.out.println(
                                p.getNumberOfIndividuals() + "\t"
                                + p.getNumberOfNonMissingIndividuals() + "\t"
                                + p.likelihood + "\t"
                                + p.logLikelihoodOfNull() + "\t"
                                + p.logModelLikelihoodOverNull() + "\t"
                                + alpha);
                        System.out.println(p.printGraph());
                        done.add(p);
                    }
                } catch (Exception ex) {
                    Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                            Level.SEVERE, null, ex);
                }
            }
        }

        System.out.println(
                "Number: " + numIndividuals);
        System.exit(
                0);

        // Compare final pedigrees
        int numIdentified = 0, numNotIdentified = 0;

        GoldStandard.createPedigreeFromFamFile(
                "Soapv2.fam");
        //System.out.println(pedigree.getNumberOfRelationships());
        System.out.println(
                "True pedigree evaluation:");
        ArrayList<Pedigree> subTruePedigrees =
                GoldStandard.getSubPedigreesOfDegreeD(4);

        for (Pedigree trueP : subTruePedigrees) {
            //System.out.println(trueP.getIndividuals());
            boolean presentInSomething = false;
            for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
                for (ProbabilisticPedigree p : list) {
                    //int num = p.numberOfRelationshipsOfSubPedigreeContained(
                    //      trueP);
                    //System.out.println(num + "\t" + trueP.
                    //        getNumberOfRelationships());
                    /*
                    num = p.numberOfIndividualsOfSubPedigreeContained(
                    trueP);
                    if (num == trueP.getNumberOfRelationships()) {
                    if (p.getNumberOfIndividuals() <= 5) {
                    //System.out.println("!!" + num + "\t" + trueP.
                    //        getNumberOfRelationships() + "\t" + trueP.
                    //        getIndividuals() + "\t" + p.getIndividuals());
                    //System.out.println("True pedigree");
                    for (Iterator<Relationship> r_iterator = trueP.
                    getRelationshipIterator();
                    r_iterator.hasNext();) {
                    Relationship r =
                    (Relationship) r_iterator.next();
                    for (Iterator<Relationship> r2_iterator = p.
                    getRelationshipIterator();
                    r2_iterator.hasNext();) {
                    Relationship r2 =
                    (Relationship) r2_iterator.next();
                    System.out.println(r + "\t" + r2 + "\t" + r.equals(r2));
                    }
                    }
                    
                    /*
                    System.out.println("True pedigree");
                    for (Iterator<Relationship> r_iterator = trueP.
                    getRelationshipIterator();
                    r_iterator.hasNext();) {
                    Relationship r =
                    (Relationship) r_iterator.next();
                    System.out.println(r + "\t" + p.
                    containsRelationship(r));
                    }
                    System.out.println("Estimated p");
                    for (Iterator<Relationship> r_iterator = p.
                    getRelationshipIterator();
                    r_iterator.hasNext();) {
                    Relationship r =
                    (Relationship) r_iterator.next();
                    System.out.println(r + "\t" + trueP.
                    containsRelationship(r));
                    }
                     * 
                    }
                    }
                    
                     */

                    if (p.containsSubPedigree(trueP)) {
                        presentInSomething = true;
                        //System.out.println("!!");
                        break;
                    }
                }
            }
            if (presentInSomething) {
                numIdentified += 1;
            } else {
                numNotIdentified += 1;
                System.out.println(trueP.printGraph());
            }
            // System.out.println(
            //       trueP.getIndividuals() + "\t" + presentInSomething);
        }

        System.out.println(
                "Total number of quads: " + subTruePedigrees.size());
        System.out.println(
                "Number of quads identified: " + numIdentified);
        System.out.println(
                "Number of quads not identified: " + numNotIdentified);

        //System.out.println(pedigreesWithCommonIndividuals);
        // iterate through those adn find the common folks

        /*
         * if only one person in common; can multiply the sub-pedigree probas
         * if 2 ppl, 1 link in common; need to divide out the common link's prob
         * if 2 ppl, no links in common - one has to be in error
         *      how to choose?
         *      
         */
    }

    private static void initializeRelationshipDistributions() {
        System.out.println(
                "Computing empirical distributions for known relationships");
        /*
        String p_c_filename = "R Files/parent_child.known.subset.data";
        String gp_gc_filename =
        "R Files/grandparent_grandchild.known.subset.data";
        String s_s_filename = "R Files/siblings.known.subset.data";
        String hs_hs_filename = "R Files/halfsiblings.known.subset.data";
        String unrel_filename = "R Files/background.known.subset.data";
         * 
         */
        //String trainingRoot = "K-503";
        /*String p_c_filename =
        "./pairs/parent_child.known." + trainingRoot + ".pairs.data";
        String gp_gc_filename =
        "./pairs/gpgc.known." + trainingRoot + ".pairs.data";
        String s_s_filename =
        "./pairs/sibs.known." + trainingRoot + ".pairs.data";
        String hs_hs_filename =
        "./pairs/halfsibs.known." + trainingRoot + ".pairs.data";
        String avun_filename =
        "./pairs/avuncular.known." + trainingRoot + ".pairs.data";
        String unrel_filename =
        "./pairs/background.known.subset.data";*/
        String p_c_filename =
                "./R Files/parent_child.known." + trainingRoot + ".pairs.data";
        String gp_gc_filename =
                "./R Files/gpgc.known." + trainingRoot + ".pairs.data";
        String s_s_filename =
                "./R Files/sibs.known." + trainingRoot + ".pairs.data";
        String hs_hs_filename =
                "./R Files/halfsibs.known." + trainingRoot + ".pairs.data";
        String avun_filename =
                "./R Files/avuncular.known." + trainingRoot + ".pairs.data";
        String unrel_filename =
                "./R Files/previous data/background.known.subset.data";
        // TODO : double check this ^^ 
        try {
            // TODO: order of individuals matters!
            /*
            parent_child = new RelationshipDistribution("PARENT_CHILD", false,
            p_c_filename);
            grandparent_grandchild = new RelationshipDistribution(
            "GRANDPARENT_GRANDCHILD", false, gp_gc_filename);
            siblings = new RelationshipDistribution("SIBLINGS", true,
            s_s_filename);
            halfsiblings = new RelationshipDistribution("HALF_SIBLINGS", true,
            hs_hs_filename);
            avuncular = new RelationshipDistribution("AVUNCULAR", true,
            avun_filename);
            unrelated =
            new RelationshipDistribution("UNRELATED", true,
            unrel_filename);
             * 
             */

            // TODO: make this some constant somewhere 
            RelationshipDistribution rd =
                    new RelationshipDistribution("PARENT_CHILD", false,
                    p_c_filename);
            System.out.println("min: " + rd.getMinValues() + " max: " + rd.getMaxValues());
            knownPairwiseDistributions.put(parent_child, rd);
            rd = new RelationshipDistribution("GRANDPARENT_GRANDCHILD", false,
                    gp_gc_filename);
            knownPairwiseDistributions.put(new RelationshipTypes(2, 0, 1), rd);
            System.out.println("Sibs");
            rd = new RelationshipDistribution("SIBLINGS", true, s_s_filename);

            /*
            for (int i = 0; i<100; i++)
            {
            if(i%10==0)
            System.out.println();
            System.out.print(rd.countDistribution.sample()+ " ");
            }
             * 
             */
            //System.out.println(rd.countDistribution.getSampleStats());
            knownPairwiseDistributions.put(new RelationshipTypes(1, 1, 2), rd);
            //rd = new RelationshipDistribution("HALF_SIBLINGS", true, hs_hs_filename);
            //knownPairwiseDistributions.put(new RelationshipTypes(1, 1), rd);
            rd = new RelationshipDistribution("AVUNCULAR", false, avun_filename);
            System.out.println("min: " + rd.getMinValues() + " max: " + rd.getMaxValues());
            knownPairwiseDistributions.put(new RelationshipTypes(2, 1, 2), rd);
            rd = new RelationshipDistribution("UNRELATED", true, unrel_filename);
            knownPairwiseDistributions.put(new RelationshipTypes(Integer.MAX_VALUE, Integer.MAX_VALUE, 0), rd);


        } catch (Exception e) {
            Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                    Level.SEVERE, null, e);
            System.exit(
                    0);
        }

        /*
        for (int i = 0; i < 6; i++) // TODO: no constants
        {
        relationships.add(new HashMap<String, ProbabilisticRelationship>());
        }
         * 
         */

    }

    private static void readTrios(String filename,
            RelationshipTypes rt12, RelationshipTypes rt23,
            RelationshipTypes rt13, int direction12, int direction23,
            int direction13) {
        /* trio files: 
         * id1 id2 id3 length12 count12 length23 count23 length13 count13
         */
        //System.out.println(rt12 + " " + rt23 + " " + rt13);
        BufferedReader in = null;
        String line = null;

        try {
            in = new BufferedReader(new FileReader(filename));

            while ((line = in.readLine()) != null) {
                // Ignore header line
                if (line.startsWith("id")) {
                    continue;
                }
                //line = line.replaceAll("\"", "");
                //TODO: standardize file input formats
                String[] fields = line.split(",");
                //System.out.println(line);
                Individual i1 = individuals.get(fields[0].trim());
                Individual i2 = individuals.get(fields[1].trim());
                Individual i3 = individuals.get(fields[2].trim());
                if (i1 == null) {
                    Individual i = new Individual(fields[0].trim());
                    individuals.put(i.id, i);
                    throw new Exception(
                            fields[0].trim() + " not present in list of individuals");
                }
                if (i2 == null) {
                    Individual i = new Individual(fields[1].trim());
                    individuals.put(i.id, i);
                    throw new Exception(
                            fields[1].trim() + " not present in list of individuals");
                }
                if (i3 == null) {
                    Individual i = new Individual(fields[2].trim());
                    individuals.put(i.id, i);
                    throw new Exception(
                            fields[2].trim() + " not present in list of individuals");
                }

                ProbabilisticPedigree p = new ProbabilisticPedigree();

                // IDs aren't already read in the pair probabilities file
                // Accounting for different ordering
                String key12 = createOrderedKeyWithRelationship(i1, i2, rt12),
                        key23 = createOrderedKeyWithRelationship(i2, i3, rt23),
                        key13 = createOrderedKeyWithRelationship(i1, i3, rt13);
                //System.out.println(key12+"\n"+key23+"\n"+key13);

                ProbabilisticRelationship i1_i2_temp = relationships.get(key12);
                ProbabilisticRelationship i2_i3_temp = relationships.get(key23);
                ProbabilisticRelationship i1_i3_temp = relationships.get(key13);


                RelationshipDistribution dist12 =
                        knownPairwiseDistributions.get(rt12);
                RelationshipDistribution dist23 =
                        knownPairwiseDistributions.get(rt23);
                RelationshipDistribution dist13 =
                        knownPairwiseDistributions.get(rt13);

                ProbabilisticRelationship i1_i2 = null;
                if (i1_i2_temp == null) {
                    if (direction12 == 1) {
                        i1_i2 = new ProbabilisticRelationship(i1, i2,
                                Double.parseDouble(fields[4].trim()),
                                Integer.parseInt(fields[3].trim()), rt12);
                    } else if (direction12 == -1) {
                        i1_i2 = new ProbabilisticRelationship(i2, i1,
                                Double.parseDouble(fields[4].trim()),
                                Integer.parseInt(fields[3].trim()), rt12);
                    }
                    relationships.put(key12, i1_i2);
                } else {
                    if (direction12 == 1) {
                        i1_i2 = new ProbabilisticRelationship(i1, i2,
                                i1_i2_temp.totalSharedIBD,
                                i1_i2_temp.numberOfSharedSegments, rt12);
                    } else if (direction12 == -1) {
                        i1_i2 = new ProbabilisticRelationship(i2, i1,
                                i1_i2_temp.totalSharedIBD,
                                i1_i2_temp.numberOfSharedSegments, rt12);
                    }
                }
                i1_i2.setRelationshipDistribution(dist12);
                i1_i2.calculateRelationshipLikelihood();
                p.addRelationship(i1_i2, dist12.symmetric);

                ProbabilisticRelationship i2_i3 = null;
                if (i2_i3_temp == null) {
                    if (direction23 == 1) {
                        i2_i3 = new ProbabilisticRelationship(i2, i3,
                                Double.parseDouble(fields[6].trim()),
                                Integer.parseInt(fields[5].trim()), rt23);
                    } else if (direction23 == -1) {
                        i2_i3 = new ProbabilisticRelationship(i3, i2,
                                Double.parseDouble(fields[6].trim()),
                                Integer.parseInt(fields[5].trim()), rt23);
                    }

                    relationships.put(key23, i2_i3);
                } else {
                    if (direction23 == 1) {
                        i2_i3 = new ProbabilisticRelationship(i2, i3,
                                i2_i3_temp.totalSharedIBD,
                                i2_i3_temp.numberOfSharedSegments, rt23);
                    } else if (direction23 == -1) {
                        i2_i3 = new ProbabilisticRelationship(i3, i2,
                                i2_i3_temp.totalSharedIBD,
                                i2_i3_temp.numberOfSharedSegments, rt23);
                    }
                }
                i2_i3.setRelationshipDistribution(dist23);
                i2_i3.calculateRelationshipLikelihood();
                p.addRelationship(i2_i3, dist23.symmetric);


                ProbabilisticRelationship i1_i3 = null;
                if (i1_i3_temp == null) {
                    if (direction13 == 1) {
                        i1_i3 = new ProbabilisticRelationship(i1, i3,
                                Double.parseDouble(fields[8].trim()),
                                Integer.parseInt(fields[7].trim()), rt13);
                    } else if (direction13 == -1) {
                        i1_i3 = new ProbabilisticRelationship(i3, i1,
                                Double.parseDouble(fields[8].trim()),
                                Integer.parseInt(fields[7].trim()), rt13);
                    }
                    relationships.put(key13, i1_i3);
                } else {
                    if (direction13 == 1) {
                        i1_i3 = new ProbabilisticRelationship(i1, i3,
                                i1_i3_temp.totalSharedIBD,
                                i1_i3_temp.numberOfSharedSegments, rt13);
                    } else if (direction13 == -1) {
                        i1_i3 = new ProbabilisticRelationship(i3, i1,
                                i1_i3_temp.totalSharedIBD,
                                i1_i3_temp.numberOfSharedSegments, rt13);
                    }
                }
                i1_i3.setRelationshipDistribution(dist13);
                i1_i3.calculateRelationshipLikelihood();
                p.addRelationship(i1_i3, dist13.symmetric);

                // TODO: relationships are going to have multiple objects with the same key since there are 5 probabilities for each of the pairs of relationships


                // if 2 of the 3 trio relationships aren't P-C, add the missing individuals
                // 3 sibs

                if (dist12.getName().equals("SIBLINGS")
                        && dist23.getName().equals("SIBLINGS")
                        && dist13.getName().equals("SIBLINGS")) {
                    Individual missing = new Individual("K" + missingID); // TODO: hard coded
                    missingID--;
                    // TODO: handle no info better
                    ProbabilisticRelationship i1_m =
                            new ProbabilisticRelationship(missing, i1, -1, -1,
                            -1, parent_child);
                    i1_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    p.addRelationship(i1_m, false);
                    i1_m.calculateRelationshipLikelihood();

                    ProbabilisticRelationship i2_m =
                            new ProbabilisticRelationship(missing, i2, -1, -1,
                            -1, parent_child);
                    i2_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i2_m.calculateRelationshipLikelihood();
                    p.addRelationship(i2_m, false);

                    ProbabilisticRelationship i3_m =
                            new ProbabilisticRelationship(missing, i3, -1, -1,
                            -1, parent_child);
                    i3_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i3_m.calculateRelationshipLikelihood();
                    p.addRelationship(i3_m, false);

                    missing = new Individual("K" + missingID); // TODO: hard coded
                    missingID--;
                    // TODO: handle no info better
                    i1_m =
                            new ProbabilisticRelationship(missing, i1, -1, -1,
                            -1, parent_child);
                    i1_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    p.addRelationship(i1_m, false);
                    i1_m.calculateRelationshipLikelihood();

                    i2_m =
                            new ProbabilisticRelationship(missing, i2, -1, -1,
                            -1, parent_child);
                    i2_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i2_m.calculateRelationshipLikelihood();
                    p.addRelationship(i2_m, false);

                    i3_m =
                            new ProbabilisticRelationship(missing, i3, -1, -1,
                            -1, parent_child);
                    i3_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i3_m.calculateRelationshipLikelihood();
                    p.addRelationship(i3_m, false);
                    //System.out.println("missing: " + missingID);
                } else if ((dist12.getName().equals("PARENT_CHILD")
                        || dist13.getName().equals("PARENT_CHILD"))
                        && dist23.getName().equals("SIBLINGS")) { // avuncular
                    Individual missing = new Individual("K" + missingID); // TODO: hard coded
                    missingID--;
                    ProbabilisticRelationship i2_m =
                            new ProbabilisticRelationship(missing, i2, -1, -1,
                            -1, parent_child);
                    i2_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i2_m.calculateRelationshipLikelihood();
                    p.addRelationship(i2_m, false);

                    ProbabilisticRelationship i3_m =
                            new ProbabilisticRelationship(missing, i3, -1, -1,
                            -1, parent_child);
                    i3_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i3_m.calculateRelationshipLikelihood();
                    p.addRelationship(i3_m, false);

                    missing = new Individual("K" + missingID); // TODO: hard coded
                    missingID--;
                    i2_m =
                            new ProbabilisticRelationship(missing, i2, -1, -1,
                            -1, parent_child);
                    i2_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i2_m.calculateRelationshipLikelihood();
                    p.addRelationship(i2_m, false);

                    i3_m =
                            new ProbabilisticRelationship(missing, i3, -1, -1,
                            -1, parent_child);
                    i3_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i3_m.calculateRelationshipLikelihood();
                    p.addRelationship(i3_m, false);

                } else if ((dist12.getName().equals("PARENT_CHILD")
                        || dist23.getName().equals("PARENT_CHILD"))
                        && dist13.getName().equals("SIBLINGS")) { // parent-2 sibs
                    Individual missing = new Individual("K" + missingID); // TODO: hard coded
                    missingID--;
                    //System.out.println("12 -pc || 23 -pc   && 13 -sib");
                    ProbabilisticRelationship i1_m =
                            new ProbabilisticRelationship(missing, i1, -1, -1,
                            -1, parent_child);
                    i1_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i1_m.calculateRelationshipLikelihood();
                    p.addRelationship(i1_m, false);

                    ProbabilisticRelationship i3_m =
                            new ProbabilisticRelationship(missing, i3, -1, -1,
                            -1, parent_child);
                    i3_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i3_m.calculateRelationshipLikelihood();
                    p.addRelationship(i3_m, false);

                    /*Individual p1missing = new Individual("K" + missingID); // TODO: hard coded
                    missingID--;
                    ProbabilisticRelationship p1_m =
                    new ProbabilisticRelationship(p1missing, missing, -1, -1,
                    -1, parent_child);
                    p1_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    p1_m.calculateRelationshipLikelihood();
                    p.addRelationship(p1_m, false);
                    
                    Individual p2missing = new Individual("K" + missingID); // TODO: hard coded
                    missingID--;
                    ProbabilisticRelationship p2_m =
                    new ProbabilisticRelationship(p2missing, missing, -1, -1,
                    -1, parent_child);
                    p2_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    p2_m.calculateRelationshipLikelihood();
                    p.addRelationship(p2_m, false);
                    
                    
                     * 
                     */
                } else if ((dist13.getName().equals("PARENT_CHILD")
                        || dist23.getName().equals("PARENT_CHILD"))
                        && dist12.getName().equals("SIBLINGS")) {
                    Individual missing = new Individual("K" + missingID); // TODO: hard coded
                    missingID--;
                    System.out.println("Does this ever happen??");
                    System.out.println("13 -pc || 23 -pc   && 12 -sib");
                    ProbabilisticRelationship i1_m =
                            new ProbabilisticRelationship(missing, i1, -1, -1,
                            -1, parent_child);
                    i1_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    p.addRelationship(i1_m, false);
                    i1_m.calculateRelationshipLikelihood();

                    ProbabilisticRelationship i2_m =
                            new ProbabilisticRelationship(missing, i2, -1, -1,
                            -1, parent_child);
                    i2_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                    i2_m.calculateRelationshipLikelihood();
                    p.addRelationship(i2_m, false);
                }

                // if individuals don't have parents; add missing parents

                // add parents of siblings first
                /*ArrayList<ProbabilisticRelationship> RtoAdd =
                new ArrayList<ProbabilisticRelationship>();
                for (Relationship r : p.getRelationships()) {
                if (r.relationshipType.equals(PedigreeReconstruction.siblings)) {
                int num_parents = p.getParents(r.individual1).size();
                while (num_parents < 2) {
                Individual missing = new Individual("K" + missingID); // TODO: hard coded
                missingID--;
                ProbabilisticRelationship i1_m =
                new ProbabilisticRelationship(missing, r.individual1, -1, -1,
                -1, parent_child);
                i1_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                //p.addRelationship(i1_m, false);
                i1_m.calculateRelationshipLikelihood();
                RtoAdd.add(i1_m);
                ProbabilisticRelationship i2_m =
                new ProbabilisticRelationship(missing, r.individual2, -1, -1,
                -1, parent_child);
                i2_m.setRelationshipDistribution(knownPairwiseDistributions.get(parent_child));
                i2_m.calculateRelationshipLikelihood();
                RtoAdd.add(i2_m);
                num_parents++;
                //p.addRelationship(i2_m, false);
                }
                }
                }
                
                for (ProbabilisticRelationship r : RtoAdd) {
                p.addRelationship(r, false); // always will be parent-child
                }
                 * */
                HashMap<Individual, Integer> toAdd =
                        new HashMap<Individual, Integer>();
                for (Individual i : p.getIndividuals()) {
                    if (!i.isMissing() && p.getParents(i).size() < 2) {
                        toAdd.put(i, 2 - p.getParents(i).size());
                    }

                }
                //System.out.println(p.printGraph());
                for (Individual i : toAdd.keySet()) {
                    //System.out.println(i + "\t" + toAdd.get(i));
                    for (int count = 0; count < toAdd.get(i); count++) {
                        missingID = p.addMissing(i, missingID);
                        // TODO: don't understand why this changes things
                    }
                }

                p.computeLogProbability();

                if (!p.isConsistent()) {
                    throw new Exception("Input trio is inconsistent: " + rt12 + " " + rt23
                            + " " + rt13 + "\n" + p.printGraph());
                }

                for (Iterator<Relationship> r_iterator =
                        p.getRelationshipIterator(); r_iterator.hasNext();) {
                    ProbabilisticRelationship r =
                            (ProbabilisticRelationship) r_iterator.next();
                    TreeSet<ProbabilisticPedigree> prev =
                            pedigrees.get(r);
                    if (prev == null) {
                        prev = new TreeSet<ProbabilisticPedigree>();
                    }

                    /*
                    boolean alreadyAdded = false;
                    for (Individual i : p.getIndividuals()) {
                    ArrayList<ProbabilisticPedigree> list =
                    pedigrees.get(i.hashCode());
                    if (list != null) {
                    if (list.contains(p)) {
                    /*System.out.println("Duplicate");
                    System.out.println(p.printGraph() + "List (" + i + "):");
                    for (ProbabilisticPedigree temp : list) {
                    System.out.println(temp.equals(p) + "\t" + temp.printGraph());
                    }
                     * 
                     *
                    alreadyAdded = true;
                    break;
                    }
                    }
                    }
                     * 
                     */
                    //if (!alreadyAdded) {
                    prev.add(p);
                    //pedigrees.put(p.mostConnectedIndividual().hashCode(), prev);
                    pedigrees.put(r, prev);
                    //}
                }
            }
            System.out.println("missing id: " + missingID);
        } catch (IOException ex) {
            Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                    Level.SEVERE, null, ex);
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
            Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                    Level.SEVERE, null, ex);
        } finally {
            try {
                in.close();


            } catch (IOException ex) {
                Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                        Level.SEVERE, null, ex);
            }
        }

    }

    private static HashMap<String, HashMap<String, Pedigree>> getPedigreesWithCommonIndividuals2(
            HashMap<Integer, ArrayList<ProbabilisticPedigree>> pedigrees) {
        HashMap<String, HashMap<String, Pedigree>> toCombine =
                new HashMap<String, HashMap<String, Pedigree>>();
        //ArrayList<Pedigree> alreadyCombined = new ArrayList<Pedigree>();
        HashMap<Integer, Integer> alreadyCombined =
                new HashMap<Integer, Integer>();
        // compare every pair of smaller pedigrees

        System.out.println("Looking for common pedigrees");
        for (ArrayList<ProbabilisticPedigree> listOfPedigrees :
                pedigrees.values()) {
            Iterator<ProbabilisticPedigree> iter1 = listOfPedigrees.iterator();
            while (iter1.hasNext()) {
                HashMap<String, Pedigree> p = new HashMap<String, Pedigree>();
                Pedigree p1;
                p1 = iter1.next();
                if (alreadyCombined.containsKey(p1.hashCode())) {
                    continue;
                }
                for (Individual i : p1.getIndividuals()) {
                    if (pedigrees.get(i.hashCode()) != null) {
                        Iterator<ProbabilisticPedigree> iter2 =
                                pedigrees.get(i.hashCode()).iterator();
                        while (iter2.hasNext()) {
                            Pedigree p2;
                            p2 = iter2.next();
                            if (alreadyCombined.containsKey(p2.hashCode())) {
                                continue;
                            }
                            if (p1.equals(p2)) {
                                continue;
                            }
                            if (p1.hasCommonIndividuals(p2)) {
                                p.put(p1.founder(), p1);
                                p.put(p2.founder(), p2);
                                alreadyCombined.put(p1.hashCode(), 1);
                                alreadyCombined.put(p2.hashCode(), 1);
                                toCombine.put(p1.founder(), p);
                            }
                        }
                    }
                }
            }
        }

        System.out.println(toCombine.size());
        return toCombine;
    }

    private static void parseArgs(String[] args) {
        Options options = new Options();
        options.addOption("pair_pc", true,
                "csv of pairwise probabalities of parent-child relationships");
        options.addOption("pair_gpgc", true,
                "csv of pairwise probabalities of grandparent-grandchild relationships");
        options.addOption("pair_sibs", true,
                "csv of pairwise probabalities of sibling-sibling relationships");
        options.addOption("pair_halfsibs", true,
                "csv of pairwise probabalities of half sibling-half sibling relationships");
        options.addOption("pair_avuncular", true,
                "csv of pairwise probabalities of avuncular (uncle-nephew) relationships");
        options.addOption("pair_unrel", true,
                "csv of pairwise probabalities of unrelateds");

        options.addOption("trio_gp_p_c", true,
                "csv of trio probabalities of grandparent-parent-grandchild relationships");
        options.addOption("trio_p_c_p", true,
                "csv of trio probabalities of parent1-child-parent2 relationships");
        options.addOption("trio_c_p_c", true,
                "csv of trio probabalities of child1-parent-child2 relationships");
        options.addOption("trio_p_s_c", true,
                "csv of trio probabalities of parent-parent's sibling-child relationships");
        options.addOption("trio_s_s_s", true,
                "csv of trio probabalities of sibling-sibling-sibling relationships");
        options.addOption("trainingRoot", true,
                "Individual who is root of training pedigree");
        options.addOption("ppllist", true,
                "List of ppl");

        try {
            CommandLineParser parser = new GnuParser();
            CommandLine cmd = parser.parse(options, args);

            // TODO: for now this order matches the enum order but need to find a way to ensure that
            pairFileNames.add(cmd.getOptionValue("pair_pc"));
            pairFileNames.add(cmd.getOptionValue("pair_gpgc"));
            pairFileNames.add(cmd.getOptionValue("pair_sibs"));
            pairFileNames.add(cmd.getOptionValue("pair_halfsibs"));
            pairFileNames.add(cmd.getOptionValue("pair_unrel"));

            trioFileNames.add(cmd.getOptionValue("trio_gp_p_c"));
            trioFileNames.add(cmd.getOptionValue("trio_p_c_p"));
            trioFileNames.add(cmd.getOptionValue("trio_c_p_c"));
            trioFileNames.add(cmd.getOptionValue("trio_p_s_c"));
            trioFileNames.add(cmd.getOptionValue("trio_s_s_s"));

            trainingRoot = cmd.getOptionValue("trainingRoot");

            ppllist = cmd.getOptionValue("ppllist");



        } catch (ParseException ex) {
            Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                    Level.SEVERE, null, ex);
        }
    }

    private static void combinePedigreesWithCommonIndividuals(
            HashMap<String, HashMap<String, Pedigree>> pedigreesWithCommonIndividuals,
            boolean VERBOSE) {

        for (Iterator<HashMap<String, Pedigree>> it =
                pedigreesWithCommonIndividuals.values().iterator();
                it.hasNext();) {
            HashMap<String, Pedigree> toCombinePedigrees =
                    (HashMap<String, Pedigree>) it.next();
            try {
                combinePedigrees(toCombinePedigrees, VERBOSE);


            } catch (Exception ex) {
                Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        // remove pedigrees that are sub-pedigrees of others
        //removeSubPedigrees();
    }

    // need to get combinations sharing the same edge
    private static HashMap<String, HashMap<String, Pedigree>> getPedigreesWithCommonIndividuals(
            HashMap<Integer, ArrayList<ProbabilisticPedigree>> pedigrees) {
        HashMap<String, HashMap<String, Pedigree>> toCombine =
                new HashMap<String, HashMap<String, Pedigree>>();
        HashMap<Integer, Integer> alreadyCombined =
                new HashMap<Integer, Integer>();
        // compare every pair of smaller pedigrees

        System.out.println("Looking for common pedigrees");
        for (ArrayList<ProbabilisticPedigree> listOfPedigrees : pedigrees.values()) {
            for (ProbabilisticPedigree p1 : listOfPedigrees) {
                HashMap<String, Pedigree> p = new HashMap<String, Pedigree>();
                if (alreadyCombined.containsKey(p1.hashCode())) {
                    continue;
                }
                for (Individual i : p1.getIndividuals()) {
                    if (pedigrees.get(i.hashCode()) != null) {
                        for (ProbabilisticPedigree p2 : pedigrees.get(i.hashCode())) {
                            if (alreadyCombined.containsKey(p2.hashCode())) {
                                continue;
                            }
                            if (p1.equals(p2)) {
                                continue;
                            }
                            if (p1.hasCommonRelationship(p2)) {
                                //if (p1.hasCommonIndividuals(p2)) {
                                p.put(p1.founder(), p1);
                                p.put(p2.founder(), p2);
                                alreadyCombined.put(p1.hashCode(), 1);
                                alreadyCombined.put(p2.hashCode(), 1);
                                toCombine.put(p1.founder(), p);
                            }
                        }
                    }
                }
            }
        }

        /*
        for (HashMap<String, Pedigree> list : toCombine.values())
        {
        System.out.println("Set");
        for (Pedigree p : list.values())
        System.out.println(p.printGraph());
        }
         * 
         */
        //System.out.println(toCombine.size());
        return toCombine;
    }

    private static HashSet<HashSet<Pedigree>> getPedigreesWithCommonIndividualsMaxFirst(
            HashMap<ProbabilisticRelationship, TreeSet<ProbabilisticPedigree>> pedigrees) {
        //HashMap<String, HashMap<String, Pedigree>> toCombine =
        //        new HashMap<String, HashMap<String, Pedigree>>();
        HashSet<HashSet<Pedigree>> toCombine =
                new HashSet<HashSet<Pedigree>>();
        HashSet<ProbabilisticPedigree> alreadyCombined =
                new HashSet<ProbabilisticPedigree>();
        // compare every pair of smaller pedigrees

        // Arraylist or treeset for sortedFinalPedigrees doesnt make a diff
        TreeSet<ProbabilisticPedigree> sortedFinalPedigrees =
                getListOfPedigrees(pedigrees);

        // Not quite - need to search second level in order as well
        System.out.println("Looking for common pedigrees");
        for (ProbabilisticPedigree p1 : sortedFinalPedigrees) {
            //HashMap<String, Pedigree> p = new HashMap<String, Pedigree>();
            HashSet<Pedigree> p = new HashSet<Pedigree>();
            if (alreadyCombined.contains(p1)) {
                continue;
            }
            /*try {
            //boolean tobreak = false;
            System.out.println("Attempting likelihood: " + p1.logModelLikelihoodOverNull());
            } catch (Exception ex) {
            Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
            }
             * 
             */

            TreeSet<ProbabilisticPedigree> potentialSortedList =
                    getPotentialPedigreesList(pedigrees, p1);
            System.out.println("Potential list size: " + potentialSortedList.size());
            /*System.out.println("\nPossible likelihoods: ");
            for (ProbabilisticPedigree p2 : pedigrees.get(r)) {
            try {
            System.out.print(p2.logModelLikelihoodOverNull() + "\t");
            } catch (Exception ex) {
            Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
            }
            }
             * 
             */
            /* Attempt with merging with lots of shared edges
             * int maxNumShared = 0;
            ProbabilisticPedigree maxSharedPedigree = null;
             * 
             */

            int i = 0;
            for (ProbabilisticPedigree p2 : potentialSortedList /*pedigrees.get(r)*/) {
                i++;
                try {
                    System.out.println("Possible likelihood: " + p2.logModelLikelihoodOverNull());
                } catch (Exception ex) {
                    Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
                }

                if (!checkBasicsOfPotentialCombination(p1, p2, alreadyCombined)) {
                    continue;
                }
                alreadyCombined.add(p1);
                alreadyCombined.add(p2);
                addToAlreadyAttempted(p1, p2);

                // is there at least 1 other relationship in common?
                    /*System.out.println("Attempting based on : " + r);
                try {
                System.out.println("Pedigree 1 ( " + p1.logModelLikelihoodOverNull() + " ) : " + p1.printGraph());
                System.out.println("Pedigree 2 ( " + p2.logModelLikelihoodOverNull() + " ) : " + p2.printGraph());
                } catch (Exception ex) {
                Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
                }
                 * 
                 */

                if (!checkNumberOfRelationships(p1, p2)) {
                    continue;
                }

                /*Attempt with merging with lots of shared edges
                if (numberOfCommonRelationships > maxNumShared) {
                maxNumShared = numberOfCommonRelationships;
                maxSharedPedigree = p2;
                }
                 * 
                 */
                ProbabilisticPedigree possible = null;
                try {
                    possible =
                            PedigreeReconstruction.combinePedigrees(p1, p2, false);
                } catch (Exception ex) {
                    Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
                }
                if (possible == null) {
                    System.out.println("Not consistent so not proceeding further");
                    continue;
                }


                p.add(p1);
                p.add(p2);
                System.out.println(p.size() + " **");
                System.out.println("i : " + i);
                toCombine.add(p);
                if (p.size() >= 2)// TODO: arbitrary
                {
                    break;
                }
            }

            /*if (p.size() >= 2)// TODO: arbitrary
            {
            break;
            }
             * 
             */
        }

        //if (tobreak) {
        //    break;
        //}


        return toCombine;
    }

    private static HashMap<String, HashMap<String, Pedigree>> getPedigreesWithCommonIndividualsMaxFirstOriginal(
            HashMap<ProbabilisticRelationship, ArrayList<ProbabilisticPedigree>> pedigrees) {
        HashMap<String, HashMap<String, Pedigree>> toCombine =
                new HashMap<String, HashMap<String, Pedigree>>();
        HashMap<Integer, Integer> alreadyCombined =
                new HashMap<Integer, Integer>();
        // compare every pair of smaller pedigrees

        ArrayList<ProbabilisticPedigree> sortedFinalPedigrees =
                new ArrayList<ProbabilisticPedigree>();
        //Collections.sort(sortedFinalPedigrees);
        for (ArrayList<ProbabilisticPedigree> list : pedigrees.values()) {
            for (ProbabilisticPedigree p : list) {
                sortedFinalPedigrees.add(p);
            }
        }
        Collections.sort(sortedFinalPedigrees, new Comparator<ProbabilisticPedigree>() {

            @Override
            public int compare(ProbabilisticPedigree a, ProbabilisticPedigree b) {
                int diff = 0;
                try {
                    //return (int) (-a.likelihood * 10e6 + b.likelihood * 10e6);
                    //return a.getNumberOfIndividuals() - b.getNumberOfIndividuals();
                    diff =
                            (int) (a.logModelLikelihoodOverNull() * 10e6
                            - b.logModelLikelihoodOverNull() * 10e6);
                } catch (Exception ex) {
                    Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
                }
                return diff;
            }
        });

        /*
        System.out.println("--------");
        for (ProbabilisticPedigree p : sortedFinalPedigrees) {
        System.out.println(p.likelihood);
        }
         * 
         */


        // Not quite - need to search second level in order as well
        System.out.println("Looking for common pedigrees");
        for (ProbabilisticPedigree p1 : sortedFinalPedigrees) {
            //System.exit(0);
            HashMap<String, Pedigree> p = new HashMap<String, Pedigree>();
            ProbabilisticPedigree ped_p = new ProbabilisticPedigree();
            if (alreadyCombined.containsKey(p1.hashCode())) {
                continue;
            }
            TreeSet<ProbabilisticPedigree> listOfAttempts =
                    alreadyAttemptedCombination.get(p1);
            boolean tobreak = false;
            for (Individual i : p1.getIndividuals()) {
                if (pedigrees.get(i.hashCode()) == null) {
                    continue;
                }
                int numberOfCommonRelationships = 0;
                for (ProbabilisticPedigree p2 : pedigrees.get(i.hashCode())) {
                    if (alreadyCombined.containsKey(p2.hashCode())) {
                        continue;
                    }
                    /*if (listOfAttempts!=null)
                    {
                    System.out.println("maybe");
                    }
                     * 
                     */
                    if (listOfAttempts != null
                            && listOfAttempts.contains(p2)) {
                        //System.out.println("here");
                        //System.out.println(listOfAttempts);
                        //System.out.println(p2.getNonMissingRelationships());
                        continue;
                    }
                    if (p1.equals(p2)) {
                        continue;
                    }
                    if (p1.hasCommonRelationship(p2)) {
                        numberOfCommonRelationships++;

                        ProbabilisticPedigree tempP = null;
                        try {
                            tempP =
                                    PedigreeReconstruction.combinePedigrees(ped_p, p2, true);


                        } catch (Exception ex) {
                            Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        if (tempP == null) {
                            //System.out.println("ignored");
                            continue;
                        }
                        ped_p = tempP;
                        p.put(p1.founder(), p1);
                        p.put(p2.founder(), p2);
                        alreadyCombined.put(p1.hashCode(), 1);
                        alreadyCombined.put(p2.hashCode(), 1);
                        addToAlreadyAttempted(p1, p2);
                        //System.out.println(p.size() + " **");
                        toCombine.put(p1.founder(), p);
                        if (p.size() >= 2)// TODO: arbitrary
                        {
                            tobreak = true;
                            break;
                        }
                    }
                    if (p.size() >= 2)// TODO: arbitrary
                    {
                        tobreak = true;
                        break;
                    }
                }

                if (tobreak) {
                    break;
                }
            }

        }


        /*
        for (HashMap<String, Pedigree> list : toCombine.values()) {
        System.out.println(list.size() + " $$$");
        for (Pedigree p : list.values()) {
        //System.out.println(p.printGraph());
        }
        }
         * 
         */

        return toCombine;
    }

    private static void combinePedigrees(
            HashMap<String, Pedigree> toCombinePedigrees, boolean VERBOSE)
            throws Exception {

        ProbabilisticPedigree newCombinedPedigree = new ProbabilisticPedigree();
        boolean changed = false;
        // TODO: prob not necessary
        ArrayList<String> toCombinePedigreesOrderedKeys =
                new ArrayList(toCombinePedigrees.keySet());
        //Collections.shuffle(toCombinePedigreesOrderedKeys);
        // TODO: change to proper sort 
        // used to make sure the smaller pedigree is added to the bigger pedigree
        Collections.sort(toCombinePedigreesOrderedKeys, new Comparator<String>() {

            @Override
            public int compare(String a, String b) {
                return a.length() - b.length();
            }
        });
        //ArrayList<ProbabilisticPedigree> toCombinePedigreesOrdered =
        //        orderBySize(toCombinePedigrees);
        System.out.println("------------------------------------");
        for (String key : toCombinePedigreesOrderedKeys) {
            ProbabilisticPedigree p =
                    (ProbabilisticPedigree) toCombinePedigrees.get(key);
            System.out.println("Adding: " + p.likelihood
                    + " (L(null)= " + p.logLikelihoodOfNull()
                    + ", L(over null) " + p.logModelLikelihoodOverNull()
                    + " )\n" + p.printGraph());
            //System.out.println("to: " + newCombinedPedigree.printGraph());
            //for (ProbabilisticPedigree p : toCombinePedigreesOrdered) {
            //for (Iterator<Pedigree> it = toCombinePedigrees.values().iterator(); it.hasNext();) {

            //  ProbabilisticPedigree p = (ProbabilisticPedigree) it.next();
            ProbabilisticPedigree potentialNewPedigree =
                    new ProbabilisticPedigree();

            // copy over newCombinedPedigree to tempP
            for (Iterator<Relationship> r_iterator =
                    newCombinedPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                if (!r.containsMissing()) {
                    potentialNewPedigree.addRelationship(r, r.isRelationshipSymmetric());
                }
            }

            // add the relationships in p
            // first add those relationships which don't have missing individuals
            for (Iterator<Relationship> r_iterator = p.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                if (!r.containsMissing()) {
                    if (!potentialNewPedigree.containsRelationship(r)) {
                        potentialNewPedigree.addRelationship(r, r.isRelationshipSymmetric());
                    }
                }
            }

            // TODO: remove - debugging statement
            for (Iterator<Relationship> r_iterator =
                    potentialNewPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                if (r.containsMissing()) {
                    System.out.println("!!" + r);
                }
            }

            /*
            if (!potentialNewPedigree.isConsistent()) {
            System.out.println("Inconsistent_1!!");
            System.out.println(potentialNewPedigree.printGraph());
            //System.out.println(p.printGraph());
            continue;
            }
             * 
             */
            ProbabilisticPedigree temp2 =
                    new ProbabilisticPedigree(potentialNewPedigree);
            if (!temp2.equals(potentialNewPedigree)) {
                throw new Exception("Incorrect Copy");
            }
            HashMap<Individual, Boolean> done =
                    new HashMap<Individual, Boolean>();
            for (Individual i1 : potentialNewPedigree.getIndividuals()) {
                done.put(i1, Boolean.TRUE);
                for (Individual i2 : potentialNewPedigree.getIndividuals()) {
                    /* if relationship does not exist between i1 and i2, 
                     * add appropriate relationship
                     */
                    //System.out.println("Doing??? " + i1 + " " + i2);

                    if (i1.equals(i2) || done.containsKey(i2)) {
                        continue;
                    }
                    Relationship r = temp2.getRelationship(i1, i2);
                    if (r == null) {
                        r = temp2.getRelationship(i2, i1);
                    }
                    /*
                    if (i1.id.equals("K6108") && i2.id.equals("K6191")) {
                    System.out.println("with K6108/K6191: " + r);
                    }
                    if (i2.id.equals("K6108") && i1.id.equals("K6191")) {
                    System.out.println("with K6108/K6191: " + r);
                    }
                    if (i1.id.equals("K6108") && i2.id.equals("K6191") && r != null && r.relationshipType.equals(PedigreeReconstruction.unrelated)) {
                    System.out.println("with K6108/K6191: " + r);
                    }
                     * 
                     */
                    /*
                    if (!i1.isMissing() && !i2.isMissing()) {
                    System.out.println("Doing: " + i1 + " " + i2 + " " + r);
                    }
                     * 
                     */

                    try {
                        if (r == null) {
                            //System.out.println(potentialNewPedigree.printGraph());
                            //System.out.println("here" + temp2.getIndividuals());
                            //System.out.println("here" + potentialNewPedigree.getIndividuals());
                            temp2.addConnectingRelationship(i1, i2, VERBOSE);
                        } else if (r.relationshipType.equals(PedigreeReconstruction.unrelated)) {
                            //System.out.println("here2" + temp2.getIndividuals());
                            temp2.addConnectingRelationship(i1, i2, VERBOSE);




                        }
                    } catch (Exception ex) {
                        Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }

            potentialNewPedigree = temp2;

            // then add the relationships with missing indiv.
            for (Iterator<Relationship> r_iterator =
                    newCombinedPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                boolean relationExists =
                        (potentialNewPedigree.getRelationship(r.individual1, r.individual2) != null)
                        || (potentialNewPedigree.getRelationship(r.individual2, r.individual1) != null);
                if (!relationExists && r.relationshipTypeEquals("PARENT_CHILD") && r.containsMissing()) {
                    // if non-missing person doesn't have two parents
                    Individual nonMissing = r.getNonMissingIndividual();
                    // if its a parent-child relationships and there isnt already enough parent-child for that indiv
                    if (potentialNewPedigree.containsIndividual(nonMissing)) {
                        if (potentialNewPedigree.getParents(nonMissing).size() < 2) {
                            potentialNewPedigree.addRelationship(r, r.isRelationshipSymmetric());
                        }
                    } else {
                        throw new Exception("Incorrect execution path");
                    }
                }

            }

            /*
            if (!potentialNewPedigree.isConsistent()) {
            System.out.println("Inconsistent_2!!");
            System.out.println(potentialNewPedigree.printGraph());
            //System.out.println(p.printGraph());
            continue;
            }
             * 
             */
            // add the siblings parents
            // if they are siblings; add the same missing parents
            ArrayList<ProbabilisticRelationship> rs =
                    new ArrayList<ProbabilisticRelationship>();
            for (Iterator<Relationship> r_iterator =
                    potentialNewPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                if (!r.relationshipType.equals(PedigreeReconstruction.siblings)) {
                    continue;
                }
                if (potentialNewPedigree.getParents(r.individual1).size() == 2
                        && potentialNewPedigree.getParents(r.individual2).size() == 2) {
                    continue;
                }
                if (potentialNewPedigree.getParents(r.individual1).size() == 2) {
                    // copy the missing parents from i1 to i2
                    for (Individual parent : potentialNewPedigree.getParents(r.individual1)) {
                        if (parent.isMissing()) {
                            ProbabilisticRelationship r_toadd =
                                    new ProbabilisticRelationship(parent, r.individual2, -1, -1,
                                    -1, PedigreeReconstruction.parent_child);
                            r_toadd.setRelationshipDistribution(
                                    PedigreeReconstruction.knownPairwiseDistributions.get(PedigreeReconstruction.parent_child));
                            // should never be going into this
                            if (potentialNewPedigree.containsRelationship(r_toadd)) {
                                continue;
                                //throw (new Exception("Erroneous execution path"));
                            }
                            r_toadd.calculateRelationshipLikelihood();
                            rs.add(r_toadd);
                        }
                    }
                }
                if (potentialNewPedigree.getParents(r.individual2).size() == 2) {
                    // copy the parents from i2 to i1
                    for (Individual parent : potentialNewPedigree.getParents(r.individual2)) {
                        if (parent.isMissing()) {
                            ProbabilisticRelationship r_toadd =
                                    new ProbabilisticRelationship(parent, r.individual1, -1, -1,
                                    -1, PedigreeReconstruction.parent_child);
                            r_toadd.setRelationshipDistribution(
                                    PedigreeReconstruction.knownPairwiseDistributions.get(PedigreeReconstruction.parent_child));
                            // should never be going into this
                            if (potentialNewPedigree.containsRelationship(r_toadd)) {
                                continue;
                                //throw (new Exception("Erroneous execution path " + r_toadd
                                //        + "\t" + newCombinedPedigree.printGraph()));
                            }
                            r_toadd.calculateRelationshipLikelihood();
                            rs.add(r_toadd);
                            //newCombinedPedigree.addRelationship(r_toadd, r_toadd.isRelationshipSymmetric());
                        }
                    }
                }
            }
            for (ProbabilisticRelationship r_toadd : rs) {
                potentialNewPedigree.addRelationship(r_toadd, r_toadd.isRelationshipSymmetric());
            }

            // add missing relationships in p
            for (Iterator<Relationship> r_iterator = p.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                boolean relationExists =
                        (potentialNewPedigree.getRelationship(r.individual1, r.individual2) != null)
                        || (potentialNewPedigree.getRelationship(r.individual2, r.individual1) != null);
                if (!relationExists && r.relationshipTypeEquals("PARENT_CHILD") && r.containsMissing()) {
                    Individual nonMissing = r.getNonMissingIndividual();
                    if (potentialNewPedigree.containsIndividual(nonMissing)) {
                        if (potentialNewPedigree.getParents(nonMissing).size() < 2) {
                            potentialNewPedigree.addRelationship(r, r.isRelationshipSymmetric());
                        }
                    } else {
                        throw new Exception("Incorrect execution path");
                    }

                }

            }


            /*
            if (!potentialNewPedigree.isConsistent()) {
            System.out.println("Inconsistent_3!!");
            System.out.println(potentialNewPedigree.printGraph());
            //System.out.println(p.printGraph());
            continue;
            }
             * 
             */

            if (potentialNewPedigree.equals(newCombinedPedigree)) { // nothing's been added
                System.out.println("Nothing new added");
                //System.out.println("ISSUE!!");
                // remove p then since it's been 'added' to something else
                for (Iterator<Relationship> r_iterator =
                        potentialNewPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                    ProbabilisticRelationship r =
                            (ProbabilisticRelationship) r_iterator.next();
                    //System.out.println("removing : " + p.printGraph());
                    boolean remove = pedigrees.get(r).remove(p);
                    /*if (!remove) {
                    throw new Exception(
                    "Unable to remove pedigree with individuals" + p.getIndividuals());
                    }
                     * 
                     */
                }
                continue;
            }

            // TODO: need to combine all trios with the same relationship edge at once
            // TODO: add remaining connections between other people
            // copy tempP to temp2
            /*ProbabilisticPedigree temp2 = new ProbabilisticPedigree();
            for (Iterator<Relationship> r_iterator =
            potentialNewPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
            ProbabilisticRelationship r =
            (ProbabilisticRelationship) r_iterator.next();
            temp2.addRelationship(r, r.isRelationshipSymmetric());
            }
             */

            double alpha =
                    PedigreeReconstruction.logFalseDiscoveryRate(potentialNewPedigree);
            System.out.println("FDR: " + alpha);// + " " + tempP.getNumberOfRelationships());

            if (!potentialNewPedigree.isConsistent()) {
                System.out.println("Inconsistent after adding connecting relationships");
                System.out.println(potentialNewPedigree.printGraph());
                continue;
            }
            potentialNewPedigree.computeLogProbability();
            //System.out.println("Consistent " + p.likelihood + "\n --\n");
            //System.out.println(tempP.printGraph());
            // TODO: need to check this

            //System.out.println(tempP.getNumberOfIndividuals() + " " + tempP.getNumberOfRelationships());
            try {
                if (newCombinedPedigree.getNumberOfIndividuals() == 0) { // always add in the first one without checking alpha's and such
                    for (Iterator<Relationship> r_iterator =
                            p.getRelationshipIterator(); r_iterator.hasNext();) {
                        ProbabilisticRelationship r =
                                (ProbabilisticRelationship) r_iterator.next();
                        //System.out.println("removing : " + r + " " + pedigrees.get(r) + "\n" + p + "\n" + p.printGraph());

                        boolean remove =
                                pedigrees.get(r).remove(p);
                        /*
                        if (!remove) {
                        throw new Exception(
                        "Unable to remove pedigree with individuals" + p.getIndividuals());
                        }
                         * 
                         */
                    }
                    newCombinedPedigree = potentialNewPedigree;
                    changed = true;
                } else {
                    /*double alpha =
                    PedigreeReconstruction.logFalseDiscoveryRate(potentialNewPedigree);
                    System.out.println("FDR: " + alpha);// + " " + tempP.getNumberOfRelationships());
                     * 
                     */
                    int penalty = 3;
                    System.out.println("Likelihoods: " + potentialNewPedigree.likelihood + " "
                            + newCombinedPedigree.likelihood + " "
                            + p.likelihood);
                    System.out.println("# Relationships: " + potentialNewPedigree.getNumberOfRelationships() + " "
                            + newCombinedPedigree.getNumberOfRelationships() + " "
                            + p.getNumberOfRelationships());
                    System.out.println(potentialNewPedigree.logModelLikelihoodOverNull() + " vs. "
                            + newCombinedPedigree.logModelLikelihoodOverNull() + " + "
                            + p.logModelLikelihoodOverNull() + " (= "
                            + (newCombinedPedigree.logModelLikelihoodOverNull() + p.logModelLikelihoodOverNull()) + ")");
                    System.out.println("FDR: " + alpha);
                    if ((potentialNewPedigree.logModelLikelihoodOverNull() > alpha)
                            /*&& (potentialNewPedigree.likelihood 
                            >= (newCombinedPedigree.likelihood + p.likelihood - penalty))) {*/
                            /*&& (potentialNewPedigree.logModelLikelihoodOverNull()
                            >= (newCombinedPedigree.logModelLikelihoodOverNull()
                            + p.logModelLikelihoodOverNull() - penalty))) {*/
                            && (potentialNewPedigree.logModelLikelihoodOverNull()
                            >= (newCombinedPedigree.logModelLikelihoodOverNull() - .1))
                            && (potentialNewPedigree.logModelLikelihoodOverNull()
                            >= (p.logModelLikelihoodOverNull() - .1))) {
                        //(potentialNewPedigree.likelihood >= (newCombinedPedigree.likelihood + p.likelihood - 10e-6))) {
                        //true) {
                        // adding in some fleixbility - don't want to deal with rounding errors
                        // TODO: better way to address this

                        //if ((tempP.logModelLikelihoodOverNull()
                        //        > (newCombinedPedigree.logModelLikelihoodOverNull() /*+ p.logModelLikelihoodOverNull())*/
                        //        && (tempP.logModelLikelihoodOverNull() > alpha)) {
                        //boolean remove = true;
                        for (Iterator<Relationship> r_iterator =
                                potentialNewPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                            ProbabilisticRelationship r =
                                    (ProbabilisticRelationship) r_iterator.next();
                            if (pedigrees.containsKey(r)) {
                                //System.out.println("removing : " + p.printGraph());

                                boolean remove =
                                        pedigrees.get(r).remove(p);
                            }
                            /*
                            if (!remove) {
                            throw new Exception(
                            "Unable to remove pedigree with individuals" + p.getIndividuals());
                            }
                             * 
                             */
                        }
                        System.out.println("probs ok");
                        System.out.println(potentialNewPedigree.logModelLikelihoodOverNull() + " vs. "
                                + newCombinedPedigree.logModelLikelihoodOverNull() + " + "
                                + p.logModelLikelihoodOverNull() + " (= "
                                + (newCombinedPedigree.logModelLikelihoodOverNull() + p.logModelLikelihoodOverNull()) + ")");
                        System.out.println("Partially combined: " + potentialNewPedigree.printGraph());
                        System.out.println("(old) combined: " + newCombinedPedigree.printGraph());
                        newCombinedPedigree = potentialNewPedigree;
                        changed = true;

                    } else {
                        if (potentialNewPedigree.logModelLikelihoodOverNull() <= alpha) {
                            System.out.println("probs not ok; not above fp threshold (" + alpha + "); not combining");
                            System.out.println("Combined");
                            for (Iterator<Relationship> r_iterator =
                                    potentialNewPedigree.getRelationshipIterator();
                                    r_iterator.hasNext();) {
                                ProbabilisticRelationship r =
                                        (ProbabilisticRelationship) r_iterator.next();
                                System.out.println(r.toVerboseString()
                                        + " null: " + r.calculateNullLikelihood()
                                        + " over null: " + (r.getRelationshipLikelihood() / r.calculateNullLikelihood()));
                            }

                        } else if ((potentialNewPedigree.logModelLikelihoodOverNull()
                                < (newCombinedPedigree.logModelLikelihoodOverNull() - .1))
                                || (potentialNewPedigree.logModelLikelihoodOverNull()
                                < (p.logModelLikelihoodOverNull() - .1))) {
                            System.out.println("probs not ok; combined likelihood not enough; not combining");

                            System.out.println("Combined");
                            for (Iterator<Relationship> r_iterator =
                                    potentialNewPedigree.getRelationshipIterator();
                                    r_iterator.hasNext();) {
                                ProbabilisticRelationship r =
                                        (ProbabilisticRelationship) r_iterator.next();
                                System.out.println(r.toVerboseString()
                                        + " null: " + r.calculateNullLikelihood()
                                        + " over null: " + (r.getRelationshipLikelihood() / r.calculateNullLikelihood()));
                            }
                            System.out.println("Component1");
                            for (Iterator<Relationship> r_iterator =
                                    newCombinedPedigree.getRelationshipIterator();
                                    r_iterator.hasNext();) {
                                ProbabilisticRelationship r =
                                        (ProbabilisticRelationship) r_iterator.next();
                                System.out.println(r.toVerboseString()
                                        + " null: " + r.calculateNullLikelihood()
                                        + " over null: " + (r.getRelationshipLikelihood() / r.calculateNullLikelihood()));
                            }
                            System.out.println("Component2");
                            for (Iterator<Relationship> r_iterator =
                                    p.getRelationshipIterator();
                                    r_iterator.hasNext();) {
                                ProbabilisticRelationship r =
                                        (ProbabilisticRelationship) r_iterator.next();
                                System.out.println(r.toVerboseString()
                                        + " null: " + r.calculateNullLikelihood()
                                        + " over null: " + (r.getRelationshipLikelihood() / r.calculateNullLikelihood()));
                            }

                            System.out.println(potentialNewPedigree.likelihood + " vs. "
                                    + newCombinedPedigree.likelihood + " + "
                                    + p.likelihood + " (= "
                                    + (newCombinedPedigree.likelihood + p.likelihood) + ")");
                            //System.out.println("Adding: " + potentialNewPedigree.getIndividuals());
                            //System.out.println("Partially combined: " + p.getIndividuals());
                        } else {
                            System.out.println("probs not ok; weird!; not combining");








                        }
                        //System.out.println("(old) combined: " + newCombinedPedigree.printGraph());
                    }
                }
            } catch (Exception ex) {
                Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                        Level.SEVERE, null, ex);
            }

        }

        // TODO: do we need this?
        if (changed && newCombinedPedigree.getNumberOfIndividuals() > 0) {
            for (Iterator<Relationship> r_iterator =
                    newCombinedPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                TreeSet<ProbabilisticPedigree> prev =
                        pedigrees.get(r);
                if (prev == null) {
                    prev = new TreeSet<ProbabilisticPedigree>();
                }
                //if (newCombinedPedigree.likelihood >= 0) {
                //    System.out.println(newCombinedPedigree.printGraph());
                //}
                //newCombinedPedigree.computeLogProbability();
                prev.add(newCombinedPedigree);
                //System.out.println("___Combined Pedigree\n" + newCombinedPedigree.printGraph());
                pedigrees.put(r, prev);
            }
        }
    }

    private static void readInputData(String individualsFileName) {

        System.out.println("Reading trio data");
        readIndividuals(individualsFileName);
        //System.out.println(pairFileNames);
        //System.out.println(UNRELATED + " " + pairFileNames.get(UNRELATED));
        //System.exit(0);
        readUnknowns(pairFileNames.get(UNRELATED));

        readTrios(trioFileNames.get(GP_P_GC), parent_child, parent_child,
                grandparent_grandchild, 1, 1, 1);
        readTrios(trioFileNames.get(P_C_P), parent_child, parent_child, unrelated, 1, -1, 1);
        readTrios(trioFileNames.get(C_P_C), parent_child, parent_child, siblings, -1, 1, 1);
        //readTrios(trioFileNames.get(S_P_C), siblings, parent_child, avuncular, 1, 1, 1);
        // its actually child - parent - sib (of parent) in the file 
        // TODO : standardize this
        readTrios(trioFileNames.get(S_P_C), parent_child, siblings, avuncular, -1, 1, -1);
        // TODO: S-P-C/C-P-S needs to be checked; do avuncular properly
        readTrios(trioFileNames.get(S_S_S), siblings, siblings, siblings, 1, 1, 1);

        //printEverything();
        System.out.println("Number of pedigrees (trios) inputted: " + sizeOf(pedigrees));
        System.out.println("Number of relationships             : " + relationships.size());
        System.out.println("Done reading trio data");
    }

    private static void readIndividuals(String individualsFileName) {
        BufferedReader in = null;
        try {
            in = new BufferedReader(new FileReader(individualsFileName));
            String line;
            while ((line = in.readLine()) != null) {
                Individual i = new Individual(line.trim());
                individuals.put(i.id, i);


            }
        } catch (IOException ex) {
            Logger.getLogger(RelationshipDistribution.class.getName()).log(
                    Level.SEVERE, null, ex);
        } finally {
            try {
                in.close();


            } catch (IOException ex) {
                Logger.getLogger(RelationshipDistribution.class.getName()).log(
                        Level.SEVERE, null, ex);
            }
        }

    }

    /*
    private static String createOrderedKey(Individual i1, Individual i2) {
    String key = "";
    if (i1.idNumber() < i2.idNumber()) {
    key = i1.id + "-" + i2.id;
    } else {
    key = i2.id + "-" + i1.id;
    }
    return key;
    }
     * 
     */
    public static String createOrderedKeyWithRelationship(Individual i1,
            Individual i2, RelationshipTypes rt) {
        String key = "";
        if (i1.idNumber() < i2.idNumber()) {
            key = i1.id + "-" + i2.id + "(" + rt + ")";
        } else {
            key = i2.id + "-" + i1.id + "(" + rt + ")";
        }
        return key;
    }

    private static int sizeOf(
            HashMap<ProbabilisticRelationship, TreeSet<ProbabilisticPedigree>> pedigrees) {
        int count = 0;
        for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
            count += list.size();
        }
        return count;
    }

    public static double logFalseDiscoveryRate(Pedigree p) throws Exception {
        // reject H_0 if likelihood ratio is <= k
        // this function needs to calculate that k, such that \alpha = P(L≤k)
        // \alpha = FDR (say 5%-10%)
        // P(L < k | H_0) = \alpha
        // TODO: ignoring P(L=k) for now

        //return 2; 
        // Temporary - lets just use an arbitrary value - way quicker
        return Double.NEGATIVE_INFINITY;
        // TODO - check if this is working
        /*
        RelationshipDistribution unrelatedDistribution =
        knownPairwiseDistributions.get(new RelationshipTypes(Integer.MAX_VALUE, Integer.MAX_VALUE, 0));
        //System.out.println("!!!!" + unrelated);
        //Bootstrapping method
        int N_B = 10;
        double total_alpha = 0;
        for (int n = 0; n < N_B; n++) {
        int N = 1000;
        double[] logProbs = new double[N];
        for (int i = 0; i < N; i++) {
        double logProb = 0;
        double logProbNull = 0;
        for (Iterator<Relationship> r_iterator =
        p.getRelationshipIterator();
        r_iterator.hasNext();) {
        ProbabilisticRelationship r =
        (ProbabilisticRelationship) r_iterator.next();
        double prob =
        unrelatedDistribution.getRandomRelationshipProbability();
        while (prob <= 0) {
        prob =
        unrelatedDistribution.getRandomRelationshipProbability();
        }
        if (prob > 1) {
        throw new Exception("Probability should not be > 1");
        }
        //logProbNull *= prob;
        logProbNull += Math.log10(prob);
        
        prob = r.getRandomRelationshipProbability();
        // TODO : need to check all this <= vs <
        while (prob < 0) {
        prob = r.getRandomRelationshipProbability();
        }
        if (prob > 1) {
        throw new Exception("Probability should not be > 1");
        }
        logProb += Math.log10(prob);
        }
        //System.out.println(i + "  " + logProb);
        
        // TODO: error catching
        if (logProb > 0 || logProbNull > 0) {
        System.out.println("Error! Probability should not be > 1");
        }
        logProbs[i] = logProb - logProbNull;
        
        }
        
        // TODO: looks like it might be working. need to confirm
        EmpiricalDist ped = new EmpiricalDist(logProbs);
        if (ped.inverseF(falseDiscoveryRate) == Double.NEGATIVE_INFINITY) {
        N_B--;
        } else {
        total_alpha += ped.inverseF(falseDiscoveryRate);
        }
        }
        //System.out.println(total_alpha);
        //System.out.println(ped.inverseF(falseDiscoveryRate));
        return total_alpha / N_B;
         */
    }

    private static ProbabilisticPedigree maxLikelihood(
            HashMap<ProbabilisticPedigree, Double> pedigrees) {
        ProbabilisticPedigree maxPed = null;
        double max = Double.NEGATIVE_INFINITY;
        for (ProbabilisticPedigree p : pedigrees.keySet()) {
            try {
                /*
                if (p.getNumberOfIndividuals() > max) {
                maxPed = p;
                max = p.getNumberOfIndividuals();
                }
                 * 
                 */
                if (p.logModelLikelihoodOverNull() > max) {
                    maxPed = p;
                    max = p.logModelLikelihoodOverNull();
                }
            } catch (Exception ex) {
                Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return maxPed;
    }

    private static HashMap<ProbabilisticPedigree, Double> getMaximalFinalPedigrees() {
        HashMap<ProbabilisticPedigree, Double> maxListProcessed =
                new HashMap<ProbabilisticPedigree, Double>();
        HashMap<HashMap<ProbabilisticPedigree, Double>, Double> finalSet =
                new HashMap<HashMap<ProbabilisticPedigree, Double>, Double>();

        while (finalSet.keySet().size() < 10) {

            HashMap<ProbabilisticPedigree, Double> sortedFinalPedigrees =
                    new HashMap<ProbabilisticPedigree, Double>();
            HashMap<Individual, Boolean> doneIndividuals =
                    new HashMap<Individual, Boolean>();
            HashMap<ProbabilisticPedigree, Double> finalPedigrees =
                    new HashMap<ProbabilisticPedigree, Double>();
            Double finalPedigreesLikelihood = 0.0;

            for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
                for (ProbabilisticPedigree p : list) {
                    p.combineMissing();
                    p.computeLogProbability();
                    sortedFinalPedigrees.put(p, p.likelihood);
                }
            }

            /*while (already used as max)
            get new max
            add new max to already used stuff
             * 
             */
            ProbabilisticPedigree max = maxLikelihood(sortedFinalPedigrees);
            // get maximal ped that has not already been looked at
            maxListProcessed.put(max, max.likelihood);
            while (maxListProcessed.containsKey(max)) {
                sortedFinalPedigrees.remove(max);
                max = maxLikelihood(sortedFinalPedigrees);
            }
            if (max != null) // todo: check this
            {
                maxListProcessed.put(max, max.likelihood);
            } else {
                break;
            }
            /*
            boolean gpgc = false;
            for (Relationship r : max.getRelationships())
            {
            if (r.relationshipType.equals(grandparent_grandchild)) {
            System.out.println("### " + r);
            gpgc = true;
            }
            }
            if (gpgc)
            System.out.println(max.printGraph());
            
             * 
             */
            // get rest of pedigrees that match with maximal ped
            while (max != null) {
                boolean alreadypresent = false;
                // if any of the individuals are already in some other pedigree, remove from final list
                for (Individual i : max.getIndividuals()) {
                    if (doneIndividuals.containsKey(i)) {
                        alreadypresent = true;
                        break;
                    }
                }
                if (!alreadypresent) {
                    for (Individual i : max.getIndividuals()) {
                        doneIndividuals.put(i, Boolean.TRUE);
                    }
                    finalPedigrees.put(max, max.likelihood);
                    finalPedigreesLikelihood += max.likelihood;
                }
                sortedFinalPedigrees.remove(max);
                max = maxLikelihood(sortedFinalPedigrees);
            }
            finalSet.put(finalPedigrees, finalPedigreesLikelihood);
        }

        // get best set of pedigrees
        HashMap<ProbabilisticPedigree, Double> maxList =
                new HashMap<ProbabilisticPedigree, Double>();
        Double maxProb = Double.NEGATIVE_INFINITY;
        for (HashMap<ProbabilisticPedigree, Double> list : finalSet.keySet()) {
            System.out.println("----&&---\n");
            for (ProbabilisticPedigree p : list.keySet()) {
                System.out.println(p.likelihood + "--"
                        + p.getNumberOfIndividuals() + "\n"
                        + p.printGraph());
                //DrawGraph frame = new DrawGraph(p);
                //frame.draw(p);

            }
            System.out.println(finalSet.get(list) + "\n----&&---\n");

            //for (ProbabilisticPedigree p : list.keySet()) {
            //    System.out.println(p.getIndividuals() + "\t" + p.likelihood);
            //}
            if (finalSet.get(list) > maxProb) {
                maxProb = finalSet.get(list);
                maxList = list;
            }
        }

        return maxList;
    }

    private static ProbabilisticPedigree getMaximalFinalPedigree() {
        ProbabilisticPedigree maxPed = null;
        Double maxLikelihood = Double.NEGATIVE_INFINITY;
        for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
            for (ProbabilisticPedigree p : list) {
                p.combineMissing();
                p.computeLogProbability();
                try {
                    if (p.logModelLikelihoodOverNull() > maxLikelihood) {
                        maxLikelihood = p.logModelLikelihoodOverNull();
                        maxPed = p;
                    }
                } catch (Exception ex) {
                    Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        return maxPed;
    }

    private static void updateDistributions(
            ArrayList<ProbabilisticPedigree> highlyLikelyPedigrees) {
        // for high probability pedigrees, update the distributions of the constituent parts

        System.out.println("Updating distributions with high probability information");


        for (Pedigree p : highlyLikelyPedigrees) {
            for (Iterator<Relationship> r_iterator =
                    p.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                // if known pair distribution exists for it; add to the distribution
                // ignore for the main distributions
                // else create new dist and add to known pair distribution
                if (r.relationshipType.isInTrainingData()) {
                    continue;
                }
                // Need to only include relationships that haven't previously been included

                if (relationshipsInDistributions.containsKey(r)) {
                    continue;
                }
                System.out.println(r);

                // TODO: slight bug here - known distributions get updated with some erroneous data based on a single read count at some point
                // actually might be a bigger bug - probabilities are computed with that one initial value which is useless

                if (knownPairwiseDistributions.containsKey(r.relationshipType)) {
                    RelationshipDistribution dist =
                            knownPairwiseDistributions.get(r.relationshipType);
                    dist.addDataPoint(r.numberOfSharedSegments, r.totalSharedIBD);
                    knownPairwiseDistributions.put(r.relationshipType, dist);
                } else {
                    // TODO:
                    // all other relationships are not symmetric - is this true?
                    ArrayList<Double> lengthPdf = new ArrayList<Double>();
                    lengthPdf.add(r.totalSharedIBD);
                    ArrayList<Double> countPdf = new ArrayList<Double>();
                    countPdf.add((double) r.numberOfSharedSegments);
                    RelationshipDistribution dist = null;
                    try {
                        dist =
                                new RelationshipDistribution("", false, lengthPdf, countPdf);








                    } catch (Exception ex) {
                        Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    knownPairwiseDistributions.put(r.relationshipType, dist);
                }
                relationshipsInDistributions.put(r, true);
            }
        }
        System.out.println("Number of (new) relationships in distributions: "
                + relationshipsInDistributions.size());
        System.out.println("Done updating distributions");

    }

    private static ArrayList<ProbabilisticPedigree> getHighProbabilityPedigrees() {
        ArrayList<ProbabilisticPedigree> highProb =
                new ArrayList<ProbabilisticPedigree>();
        for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
            for (ProbabilisticPedigree p : list) {
                try {
                    //System.out.println(p.logModelLikelihoodOverNull());
                    if (p.logModelLikelihoodOverNull() > HIGHLY_LIKELY) {
                        highProb.add(p);
                    }
                } catch (Exception ex) {
                    Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        return highProb;
    }

    /*
    private static void removeLowProbTrios() {
    System.out.println("Early removal of low prob trios");
    HashMap<Integer, ArrayList<ProbabilisticPedigree>> toRemove =
    new HashMap<Integer, ArrayList<ProbabilisticPedigree>>();
    for (Integer key : pedigrees.keySet()) {
    for (ProbabilisticPedigree p : pedigrees.get(key)) {
    try {
    //System.out.println(p.logModelLikelihoodOverNull() + " " + PedigreeReconstruction.logFalseDiscoveryRate(p));
    if (p.logModelLikelihoodOverNull() < PedigreeReconstruction.logFalseDiscoveryRate(p)) {
    //ArrayList<ProbabilisticPedigree> old = toRemove.get(key);
    //old.add(p);
    //toRemove.put(key, old);
    if (toRemove.containsKey(key)) {
    toRemove.get(key).add(p);
    } else {
    ArrayList<ProbabilisticPedigree> list =
    new ArrayList<ProbabilisticPedigree>();
    list.add(p);
    toRemove.put(key, list);
    
    
    }
    //pedigrees.get(key).remove(p);
    }
    } catch (Exception ex) {
    Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
    }
    }
    }
    for (Integer key : toRemove.keySet()) {
    for (ProbabilisticPedigree p : toRemove.get(key)) {
    pedigrees.get(key).remove(p);
    }
    }
    }
     */
    private static ProbabilisticPedigree combinePedigrees(
            ProbabilisticPedigree p1,
            ProbabilisticPedigree p2, boolean VERBOSE) throws Exception {
        ProbabilisticPedigree newCombinedPedigree = new ProbabilisticPedigree();

        // add the relationships in p1
        // first add those relationships which don't have missing individuals
        for (Iterator<Relationship> r_iterator = p1.getRelationshipIterator(); r_iterator.hasNext();) {
            ProbabilisticRelationship r =
                    (ProbabilisticRelationship) r_iterator.next();
            if (!r.containsMissing()) {
                if (!newCombinedPedigree.containsRelationship(r)) {
                    newCombinedPedigree.addRelationship(r, r.isRelationshipSymmetric());
                }
            }
        }
        // add the relationships in p2
        // first add those relationships which don't have missing individuals
        for (Iterator<Relationship> r_iterator = p2.getRelationshipIterator(); r_iterator.hasNext();) {
            ProbabilisticRelationship r =
                    (ProbabilisticRelationship) r_iterator.next();
            if (!r.containsMissing()) {
                if (!newCombinedPedigree.containsRelationship(r)) {
                    newCombinedPedigree.addRelationship(r, r.isRelationshipSymmetric());
                }
            }
        }

        // TODO: need to combine all trios with the same relationship edge at once
        // TODO: add remaining connections between other people
        // copy tempP to temp2
        ProbabilisticPedigree temp2 = new ProbabilisticPedigree();
        for (Iterator<Relationship> r_iterator =
                newCombinedPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
            ProbabilisticRelationship r =
                    (ProbabilisticRelationship) r_iterator.next();
            temp2.addRelationship(r, r.isRelationshipSymmetric());
        }
        HashMap<Individual, Boolean> done =
                new HashMap<Individual, Boolean>();
        if (VERBOSE) {
            System.out.println("Adding additional relationships");
        }
        for (Individual i1 : newCombinedPedigree.getIndividuals()) {
            done.put(i1, Boolean.TRUE);
            for (Individual i2 : newCombinedPedigree.getIndividuals()) {
                /* if relationship does not exist between i1 and i2, 
                 * add appropriate relationship
                 */
                if (done.containsKey(i2)) {
                    continue;
                }
                if (i1.equals(i2)) {
                    continue;
                }
                Relationship r = temp2.getRelationship(i1, i2);
                if (r == null) {
                    r = temp2.getRelationship(i2, i1);
                }
                try {
                    if (r == null) {
                        temp2.addConnectingRelationship(i1, i2, VERBOSE);
                    } else if (r.relationshipType.equals(PedigreeReconstruction.unrelated)) {
                        temp2.addConnectingRelationship(i1, i2, VERBOSE);
                    }
                } catch (Exception ex) {
                    Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

        newCombinedPedigree = temp2;
        // then add the relationships with missing indiv.
        for (Iterator<Relationship> r_iterator =
                p1.getRelationshipIterator(); r_iterator.hasNext();) {
            ProbabilisticRelationship r =
                    (ProbabilisticRelationship) r_iterator.next();
            boolean relationExists =
                    (newCombinedPedigree.getRelationship(r.individual1, r.individual2) != null)
                    || (newCombinedPedigree.getRelationship(r.individual2, r.individual1) != null);
            if (!relationExists
                    && r.relationshipType.equals(PedigreeReconstruction.parent_child)
                    && r.containsMissing()) {
                // if non-missing person doesn't have two parents

                Individual nonMissing = r.getNonMissingIndividual();
                // if its a parent-child relationships and there isnt already enough parent-child for that indiv
                if (newCombinedPedigree.containsIndividual(nonMissing)) {
                    //System.out.println(r + "\t" + nonMissing);
                    if (newCombinedPedigree.getParents(nonMissing).size() < 2) {
                        //System.out.println(r + "\t" + nonMissing + '\t' + newCombinedPedigree.getParents(nonMissing));
                        newCombinedPedigree.addRelationship(r, r.isRelationshipSymmetric());
                    }
                } else {
                    throw new Exception("Incorrect execution path");
                    //newCombinedPedigree.addRelationship(r, r.isRelationshipSymmetric());
                }
            }
        }

        //System.out.println(newCombinedPedigree.printGraph());
        // add the siblings parents
        // if they are siblings; add the same missing parents
        ArrayList<ProbabilisticRelationship> rs =
                new ArrayList<ProbabilisticRelationship>();
        for (Iterator<Relationship> r_iterator =
                newCombinedPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
            ProbabilisticRelationship r =
                    (ProbabilisticRelationship) r_iterator.next();
            if (!r.relationshipType.equals(PedigreeReconstruction.siblings)) {
                continue;
            }
            if (newCombinedPedigree.getParents(r.individual1).size() == 2
                    && newCombinedPedigree.getParents(r.individual2).size() == 2) {
                continue;
            }
            if (newCombinedPedigree.getParents(r.individual1).size() == 2) {
                // copy the missing parents from i1 to i2
                for (Individual p : newCombinedPedigree.getParents(r.individual1)) {
                    if (p.isMissing()) {
                        ProbabilisticRelationship r_toadd =
                                new ProbabilisticRelationship(p, r.individual2, -1, -1,
                                -1, PedigreeReconstruction.parent_child);
                        r_toadd.setRelationshipDistribution(
                                PedigreeReconstruction.knownPairwiseDistributions.get(PedigreeReconstruction.parent_child));
                        // should never be going into this
                        if (newCombinedPedigree.containsRelationship(r_toadd)) {
                            continue;
                            //throw (new Exception("Erroneous execution path"));
                        }
                        r_toadd.calculateRelationshipLikelihood();
                        rs.add(r_toadd);
                    }
                }
            }
            if (newCombinedPedigree.getParents(r.individual2).size() == 2) {
                // copy the parents from i2 to i1
                for (Individual p : newCombinedPedigree.getParents(r.individual2)) {
                    if (p.isMissing()) {
                        ProbabilisticRelationship r_toadd =
                                new ProbabilisticRelationship(p, r.individual1, -1, -1,
                                -1, PedigreeReconstruction.parent_child);
                        r_toadd.setRelationshipDistribution(
                                PedigreeReconstruction.knownPairwiseDistributions.get(PedigreeReconstruction.parent_child));
                        // should never be going into this
                        if (newCombinedPedigree.containsRelationship(r_toadd)) {
                            continue;
                            //throw (new Exception("Erroneous execution path " + r_toadd
                            //        + "\t" + newCombinedPedigree.printGraph()));
                        }
                        r_toadd.calculateRelationshipLikelihood();
                        rs.add(r_toadd);
                        //newCombinedPedigree.addRelationship(r_toadd, r_toadd.isRelationshipSymmetric());
                    }
                }
            }
        }
        for (ProbabilisticRelationship r_toadd : rs) {
            newCombinedPedigree.addRelationship(r_toadd, r_toadd.isRelationshipSymmetric());
        }
        //System.out.println(newCombinedPedigree.printGraph());

        // then add the relationships with missing indiv.
        for (Iterator<Relationship> r_iterator =
                p2.getRelationshipIterator(); r_iterator.hasNext();) {
            ProbabilisticRelationship r =
                    (ProbabilisticRelationship) r_iterator.next();
            boolean relationExists =
                    (newCombinedPedigree.getRelationship(r.individual1, r.individual2) != null)
                    || (newCombinedPedigree.getRelationship(r.individual2, r.individual1) != null);
            if (!relationExists && r.relationshipTypeEquals("PARENT_CHILD") && r.containsMissing()) {
                // if non-missing person doesn't have two parents
                Individual nonMissing = r.getNonMissingIndividual();
                // if its a parent-child relationships and there isnt already enough parent-child for that indiv
                if (newCombinedPedigree.containsIndividual(nonMissing)) {
                    if (newCombinedPedigree.getParents(nonMissing).size() < 2) {
                        newCombinedPedigree.addRelationship(r, r.isRelationshipSymmetric());
                    }
                } else {
                    throw new Exception("Incorrect execution path");
                    //newCombinedPedigree.addRelationship(r, r.isRelationshipSymmetric());
                }
            }
        }
        //System.out.println(newCombinedPedigree.printGraph());

        if (!newCombinedPedigree.isConsistent()) {
            //System.out.println("Inconsistent: " + newCombinedPedigree.printGraph());
            //System.out.println("from: " + p1.printGraph() + "\n" + p2.printGraph());
            return null;
        } /*else {
        System.out.println("Consistent: " + newCombinedPedigree.printGraph());
        System.out.println("from: " + p1.printGraph() + "\n" + p2.printGraph());
        }
         * 
         */

        return newCombinedPedigree;

    }

    private static ArrayList<ProbabilisticPedigree> orderBySize(
            HashMap<String, Pedigree> toCombinePedigrees) {
        ArrayList<ProbabilisticPedigree> list =
                new ArrayList<ProbabilisticPedigree>();

        for (int i = 0; i < toCombinePedigrees.size(); i++) {
            int random =
                    (int) Math.floor((Math.random() * toCombinePedigrees.size()));
            String key = (String) toCombinePedigrees.keySet().toArray()[random];
            list.add((ProbabilisticPedigree) toCombinePedigrees.get(key));
            toCombinePedigrees.remove(toCombinePedigrees.get(key));
        }
        return list;
    }

    private static void addToAlreadyAttempted(ProbabilisticPedigree p1,
            ProbabilisticPedigree p2) {
        //System.out.println("!!!");
        /*if (p1.containsIndividual(new Individual("K5951")) && p2.containsIndividual(new Individual("K5951"))) {
        System.out.println("!!!!" + p1.printGraph() + "\n" + p2.printGraph());
        }
         * 
         */
        TreeSet<ProbabilisticPedigree> list =
                alreadyAttemptedCombination.get(p1);
        if (list == null) {
            list = new TreeSet<ProbabilisticPedigree>();
        }
        list.add(p2);
        alreadyAttemptedCombination.put(p1, list);

        list = null;
        list = alreadyAttemptedCombination.get(p2);
        if (list == null) {
            list = new TreeSet<ProbabilisticPedigree>();
        }
        list.add(p1);
        alreadyAttemptedCombination.put(p2, list);
    }

    private static void readUnknowns(String filename) {
        BufferedReader in = null;
        String line = null;

        try {
            in = new BufferedReader(new FileReader(filename));

            while ((line = in.readLine()) != null) {
                // Ignore header line
                if (line.startsWith("id")) {
                    continue;
                }
                String[] fields = line.split(",");
                Individual i1 = individuals.get(fields[0].trim());
                Individual i2 = individuals.get(fields[1].trim());
                if (i1 == null) {
                    Individual i = new Individual(fields[0].trim());
                    individuals.put(i.id, i);
                    throw new Exception(
                            fields[0].trim() + " not present in list of individuals");
                }
                if (i2 == null) {
                    Individual i = new Individual(fields[1].trim());
                    individuals.put(i.id, i);
                    throw new Exception(
                            fields[1].trim() + " not present in list of individuals");
                }

                // IDs aren't already read in the pair probabilities file
                // Accounting for different ordering
                String key =
                        createOrderedKeyWithRelationship(i1, i2, PedigreeReconstruction.unrelated);
                //System.out.println(key12+"\n"+key23+"\n"+key13);

                ProbabilisticRelationship i1_i2_temp = relationships.get(key);
                RelationshipDistribution dist12 =
                        knownPairwiseDistributions.get(PedigreeReconstruction.unrelated);
                ProbabilisticRelationship i1_i2 = null;
                if (i1_i2_temp == null) {
                    i1_i2 =
                            new ProbabilisticRelationship(i1, i2,
                            Double.parseDouble(fields[3].trim()),
                            Integer.parseInt(fields[2].trim()), PedigreeReconstruction.unrelated);
                    relationships.put(key, i1_i2);
                } else {
                    i1_i2 =
                            new ProbabilisticRelationship(i1, i2,
                            i1_i2_temp.totalSharedIBD,
                            i1_i2_temp.numberOfSharedSegments, PedigreeReconstruction.unrelated);
                }
                i1_i2.setRelationshipDistribution(dist12);
                //i1_i2.calculateRelationshipLikelihood();








            }
        } catch (IOException ex) {
            Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                    Level.SEVERE, null, ex);
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
            Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                    Level.SEVERE, null, ex);
        } finally {
            try {
                in.close();




            } catch (IOException ex) {
                Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                        Level.SEVERE, null, ex);
            }
        }
    }

    private static int realSizeOf(
            HashMap<ProbabilisticRelationship, TreeSet<ProbabilisticPedigree>> pedigrees) {
        HashMap<ProbabilisticPedigree, Boolean> listOfPedigrees =
                new HashMap<ProbabilisticPedigree, Boolean>();
        for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
            for (ProbabilisticPedigree p : list) {
                listOfPedigrees.put(p, Boolean.TRUE);
            }
        }
        return listOfPedigrees.keySet().size();
    }

    private static void removeLowProbTrios() {
        HashMap<ProbabilisticPedigree, Boolean> listOfPedigrees =
                new HashMap<ProbabilisticPedigree, Boolean>();
        for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
            for (ProbabilisticPedigree p : list) {
                Boolean containsGPGC = false;
                for (Iterator<Relationship> r_iterator =
                        p.getRelationshipIterator();
                        r_iterator.hasNext();) {
                    ProbabilisticRelationship r =
                            (ProbabilisticRelationship) r_iterator.next();
                    if (r.relationshipType.equals(PedigreeReconstruction.grandparent_grandchild)) {
                        containsGPGC = true;
                        break;
                    }
                }
                listOfPedigrees.put(p, containsGPGC);
            }
        }
        for (ProbabilisticPedigree p : listOfPedigrees.keySet()) {
            try {
                System.out.println(p.logModelLikelihoodOverNull() + " " + listOfPedigrees.get(p));




            } catch (Exception ex) {
                Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    private static void printEverything() {
        System.out.println("$$$$$$$$$$");
        HashMap<ProbabilisticPedigree, Boolean> listOfPedigrees =
                new HashMap<ProbabilisticPedigree, Boolean>();
        for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
            for (ProbabilisticPedigree p : list) {
                listOfPedigrees.put(p, true);
            }
        }
        for (ProbabilisticPedigree p : listOfPedigrees.keySet()) {
            try {
                System.out.println(p.logModelLikelihoodOverNull() + " " + p.printGraph());
            } catch (Exception ex) {
                Logger.getLogger(PedigreeReconstruction.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        System.out.println("$$$$$$$$$$");

    }

    private static int getMaxPedigreeSize(
            HashMap<ProbabilisticRelationship, TreeSet<ProbabilisticPedigree>> pedigrees) {
        int maxSize = 0;
        for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
            for (ProbabilisticPedigree p : list) {
                if (p.getNumberOfNonMissingIndividuals() > maxSize) {
                    maxSize = p.getNumberOfNonMissingIndividuals();
                }
            }
        }
        return maxSize;
    }

    private static TreeSet<ProbabilisticPedigree> getListOfPedigrees(
            HashMap<ProbabilisticRelationship, TreeSet<ProbabilisticPedigree>> pedigrees) {
        TreeSet<ProbabilisticPedigree> sortedFinalPedigrees =
                new TreeSet<ProbabilisticPedigree>();

        HashSet<ProbabilisticPedigree> alreadyContains =
                new HashSet<ProbabilisticPedigree>();
        for (TreeSet<ProbabilisticPedigree> list : pedigrees.values()) {
            for (ProbabilisticPedigree p : list) {
                if (alreadyContains.contains(p)) {
                    continue;
                }
                sortedFinalPedigrees.add(p);
                alreadyContains.add(p);
            }
        }
        return sortedFinalPedigrees;
    }

    private static TreeSet<ProbabilisticPedigree> getPotentialPedigreesList(
            HashMap<ProbabilisticRelationship, TreeSet<ProbabilisticPedigree>> pedigrees,
            ProbabilisticPedigree p1) {
        TreeSet<ProbabilisticPedigree> potentialSortedList =
                new TreeSet<ProbabilisticPedigree>();
        HashSet<ProbabilisticPedigree> alreadyContains =
                new HashSet<ProbabilisticPedigree>();

        for (Iterator<Relationship> r_iterator =
                p1.getRelationshipIterator(); r_iterator.hasNext();) {
            ProbabilisticRelationship r =
                    (ProbabilisticRelationship) r_iterator.next();
            for (ProbabilisticPedigree p_temp : pedigrees.get(r)) {
                if (alreadyContains.contains(p_temp)) {
                    continue;
                }
                potentialSortedList.add(p_temp);
                alreadyContains.add(p_temp);
            }
        }
        return potentialSortedList;
    }

    private static boolean checkBasicsOfPotentialCombination(
            ProbabilisticPedigree p1, ProbabilisticPedigree p2,
            HashSet<ProbabilisticPedigree> alreadyCombined) {
        if (p1.equals(p2)) {
            //System.out.println("Equal");
            return false;
        }
        if (alreadyCombined.contains(p2)) {
            //System.out.println("Already matching with something else");
            return false;
        }
        TreeSet<ProbabilisticPedigree> listOfAttempts =
                alreadyAttemptedCombination.get(p1);
        if (listOfAttempts != null
                && listOfAttempts.contains(p2)) {
            //System.out.println("Previously tried together");
            return false;
        }
        return true;
    }

    private static boolean checkNumberOfRelationships(ProbabilisticPedigree p1,
            ProbabilisticPedigree p2) {
        int numberOfCommonRelationships =
                p1.getNumberOfCommonRelationships(p2);
        /*
        int numberOfCommonRelationships = 1; // todo: seems to have a bit of a bug here
        for (Iterator<Relationship> r2_iterator =
        p2.getRelationshipIterator(); r2_iterator.hasNext();) {
        ProbabilisticRelationship r2 =
        (ProbabilisticRelationship) r2_iterator.next();
        if (p1.containsRelationship(r2)) {
        numberOfCommonRelationships++;
        }
        }
         * 
         */
        if (p1.getNumberOfNonMissingIndividuals() > 3
                && p2.getNumberOfNonMissingIndividuals() > 3
                && numberOfCommonRelationships < 2) {
            System.out.println("Not enough common relationships");
            return false;
        }
        //System.out.println("Number of common relatinships : " + numberOfCommonRelationships);
        if (numberOfCommonRelationships
                //   < Math.min(p1.getNumberOfIndividuals(), p2.getNumberOfIndividuals()) / 2) {
                < (p1.getNumberOfIndividuals() + p2.getNumberOfIndividuals()) / 3) {
            System.out.println("Not enough common relationships : "
                    + numberOfCommonRelationships + " compared to sizes of pedigrees ("
                    + p1.getNumberOfIndividuals() + " and "
                    + p2.getNumberOfIndividuals() + ")");
            return false;
        }
        return true;
    }
}
